Compare commits
115 Commits
tags/Bugzi
...
release
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
931d8eedab | ||
|
|
2a1a9ff2df | ||
|
|
0543390bce | ||
|
|
034d214153 | ||
|
|
71605a6a9a | ||
|
|
961845c47e | ||
|
|
1c627c7812 | ||
|
|
7f17e27cf0 | ||
|
|
c17680a4e5 | ||
|
|
9bf6ea3c31 | ||
|
|
801a8d9999 | ||
|
|
1ce29d2ed0 | ||
|
|
cf5e912905 | ||
|
|
991f66a42c | ||
|
|
0b3425a639 | ||
|
|
095ae364d7 | ||
|
|
4ee5de5033 | ||
|
|
ae9b838489 | ||
|
|
188ea73fac | ||
|
|
3ad6129780 | ||
|
|
d1565f139b | ||
|
|
cb97343418 | ||
|
|
cf34e1edd4 | ||
|
|
dfd8c7a9f9 | ||
|
|
d104ac4be2 | ||
|
|
6dece5a779 | ||
|
|
6334f72206 | ||
|
|
c308e60f30 | ||
|
|
d7b0531318 | ||
|
|
eeb4968977 | ||
|
|
d78a15e213 | ||
|
|
45d22db0fb | ||
|
|
6620cb4f52 | ||
|
|
93bf4cbbfe | ||
|
|
a460954b54 | ||
|
|
4b4a7b0a1c | ||
|
|
2045a62f5f | ||
|
|
dc69be5e0f | ||
|
|
8839bff2be | ||
|
|
7b8d870520 | ||
|
|
bd80e8e97b | ||
|
|
7b799934e7 | ||
|
|
7b56b48f8b | ||
|
|
34ac30982e | ||
|
|
fb63af635f | ||
|
|
9e6c560747 | ||
|
|
96fbdb013d | ||
|
|
0d8d580d24 | ||
|
|
6e2a9397d2 | ||
|
|
efeae1d426 | ||
|
|
bdedd3cda1 | ||
|
|
0915848efe | ||
|
|
8a14f98c41 | ||
|
|
c991674af0 | ||
|
|
b32f67bbcc | ||
|
|
7ffebcee9f | ||
|
|
51e14c93a9 | ||
|
|
c5ef8c5224 | ||
|
|
95ea8913db | ||
|
|
e3cd6d1fc1 | ||
|
|
9c8381e790 | ||
|
|
f04cd31af4 | ||
|
|
2d24a33b99 | ||
|
|
166e784f13 | ||
|
|
3fe9a4989d | ||
|
|
648068e289 | ||
|
|
5ff8ee481d | ||
|
|
63fa433d8e | ||
|
|
76c7e61a68 | ||
|
|
862b59a69e | ||
|
|
c522f79ece | ||
|
|
e592827a71 | ||
|
|
fe7436ebaf | ||
|
|
e1a7f7e17d | ||
|
|
3e048fdef2 | ||
|
|
81cf65bb4a | ||
|
|
561dfdcf56 | ||
|
|
671a47eab2 | ||
|
|
46e82a1ece | ||
|
|
84ecf071e1 | ||
|
|
7e3a0bcda8 | ||
|
|
faab209902 | ||
|
|
891d00f3cf | ||
|
|
41c933f51e | ||
|
|
2aa37796fa | ||
|
|
f7ca841056 | ||
|
|
232773f5f4 | ||
|
|
76874b45d1 | ||
|
|
c56f7bb4fb | ||
|
|
8a6ccb43c2 | ||
|
|
19f1281ca6 | ||
|
|
b0ea7ee81e | ||
|
|
d7084ee4fd | ||
|
|
6893804145 | ||
|
|
2b283f97a6 | ||
|
|
3a2ac33def | ||
|
|
dc6eef1493 | ||
|
|
bec02b88eb | ||
|
|
4091110aaf | ||
|
|
a2f8f6afa8 | ||
|
|
a00b159b55 | ||
|
|
4ca79d331e | ||
|
|
3ffbe5b70c | ||
|
|
01bac7c6a0 | ||
|
|
730d946722 | ||
|
|
a2d0a7429a | ||
|
|
01092a0067 | ||
|
|
589c20624d | ||
|
|
42f2431f28 | ||
|
|
771c30f1e3 | ||
|
|
e62ffa43cc | ||
|
|
0b87c1db22 | ||
|
|
da00a15686 | ||
|
|
9628510565 | ||
|
|
65863d47ac |
26
mozilla/tools/tinderbox-configs/firefox/linux/mozconfig
Normal file
26
mozilla/tools/tinderbox-configs/firefox/linux/mozconfig
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
## hostname: fx-linux-tbox
|
||||
## uname: Linux fx-linux-tbox.build.mozilla.org 2.6.18-8.el5 #1 SMP Thu Mar 15 19:57:35 EDT 2007 i686 i686 i386 GNU/Linux
|
||||
#
|
||||
|
||||
export CFLAGS="-gstabs+"
|
||||
export CXXFLAGS="-gstabs+"
|
||||
|
||||
mk_add_options MOZ_CO_PROJECT=browser
|
||||
mk_add_options PROFILE_GEN_SCRIPT=@TOPSRCDIR@/build/profile_pageloader.pl
|
||||
mk_add_options MOZ_CO_MODULE="mozilla/tools/update-packaging"
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j1"
|
||||
|
||||
ac_add_options --enable-application=browser
|
||||
ac_add_options --enable-update-channel=release
|
||||
ac_add_options --enable-update-packaging
|
||||
|
||||
# Don't add explicit optimize flags here, set them in configure.in, see bug 407794.
|
||||
ac_add_options --enable-optimize
|
||||
ac_add_options --disable-debug
|
||||
ac_add_options --disable-tests
|
||||
|
||||
ac_add_options --enable-official-branding
|
||||
|
||||
CC=/tools/gcc/bin/gcc
|
||||
CXX=/tools/gcc/bin/g++
|
||||
268
mozilla/tools/tinderbox-configs/firefox/linux/tinder-config.pl
Normal file
268
mozilla/tools/tinderbox-configs/firefox/linux/tinder-config.pl
Normal file
@@ -0,0 +1,268 @@
|
||||
#
|
||||
## hostname: fx-linux-tbox
|
||||
## uname: Linux fx-linux-tbox.build.mozilla.org 2.6.18-8.el5 #1 SMP Thu Mar 15 19:57:35 EDT 2007 i686 i686 i386 GNU/Linux
|
||||
#
|
||||
|
||||
#- tinder-config.pl - Tinderbox configuration file.
|
||||
#- Uncomment the variables you need to set.
|
||||
#- The default values are the same as the commented variables.
|
||||
|
||||
$ENV{CVS_RSH} = "ssh";
|
||||
$ENV{MOZ_CRASHREPORTER_NO_REPORT} = '1';
|
||||
|
||||
# To ensure Talkback client builds properly on some Linux boxen where LANG
|
||||
# is set to "en_US.UTF-8" by default, override that setting here by setting
|
||||
# it to "en_US.iso885915" (the setting on ocean). Proper fix is to update
|
||||
# where xrestool is called in the build system so that 'LANG=C' in its
|
||||
# environment, according to bryner.
|
||||
$ENV{LANG} = "en_US.iso885915";
|
||||
|
||||
# $ENV{MOZ_PACKAGE_MSI}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: 0
|
||||
# Values: 0 | 1
|
||||
# Purpose: Controls whether a MSI package is made.
|
||||
# Requires: Windows and a local MakeMSI installation.
|
||||
#$ENV{MOZ_PACKAGE_MSI} = 0;
|
||||
|
||||
# $ENV{MOZ_SYMBOLS_TRANSFER_TYPE}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: scp
|
||||
# Values: scp | rsync
|
||||
# Purpose: Use scp or rsync to transfer symbols to the Talkback server.
|
||||
# Requires: The selected type requires the command be available both locally
|
||||
# and on the Talkback server.
|
||||
#$ENV{MOZ_SYMBOLS_TRANSFER_TYPE} = "scp";
|
||||
|
||||
#- PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
|
||||
$BuildAdministrator = 'build@mozilla.org';
|
||||
#$BuildAdministrator = "$ENV{USER}\@$ENV{HOST}";
|
||||
#$BuildAdministrator = ($ENV{USER} || "cltbld") . "\@" . ($ENV{HOST} || "dhcp");
|
||||
|
||||
#- You'll need to change these to suit your machine's needs
|
||||
$DisplayServer = ':0.0';
|
||||
|
||||
#- Default values of command-line opts
|
||||
#-
|
||||
#$BuildDepend = 1; # Depend or Clobber
|
||||
#$BuildDebug = 0; # Debug or Opt (Darwin)
|
||||
#$ReportStatus = 1; # Send results to server, or not
|
||||
#$ReportFinalStatus = 1; # Finer control over $ReportStatus.
|
||||
$UseTimeStamp = 0; # Use the CVS 'pull-by-timestamp' option, or not
|
||||
#$BuildOnce = 0; # Build once, don't send results to server
|
||||
#$TestOnly = 0; # Only run tests, don't pull/build
|
||||
#$BuildEmbed = 0; # After building seamonkey, go build embed app.
|
||||
#$SkipMozilla = 0; # Use to debug post-mozilla.pl scripts.
|
||||
#$BuildLocales = 0; # Do l10n packaging?
|
||||
|
||||
# Tests
|
||||
$CleanProfile = 1;
|
||||
#$ResetHomeDirForTests = 1;
|
||||
$ProductName = "Firefox";
|
||||
$VendorName = 'Mozilla';
|
||||
|
||||
$RunMozillaTests = 1; # Allow turning off of all tests if needed.
|
||||
$RegxpcomTest = 1;
|
||||
$AliveTest = 1;
|
||||
#$JavaTest = 0;
|
||||
#$ViewerTest = 0;
|
||||
#$BloatTest = 0; # warren memory bloat test
|
||||
#$BloatTest2 = 0; # dbaron memory bloat test, require tracemalloc
|
||||
#$DomToTextConversionTest = 0;
|
||||
#$XpcomGlueTest = 0;
|
||||
$CodesizeTest = 0; # Z, require mozilla/tools/codesighs
|
||||
$EmbedCodesizeTest = 0; # mZ, require mozilla/tools/codesigns
|
||||
#$MailBloatTest = 0;
|
||||
#$EmbedTest = 0; # Assumes you wanted $BuildEmbed=1
|
||||
$LayoutPerformanceTest = 0; # Tp
|
||||
$DHTMLPerformanceTest = 0; # Tdhtml
|
||||
#$QATest = 0;
|
||||
#$XULWindowOpenTest = 0; # Txul
|
||||
$StartupPerformanceTest = 0; # Ts
|
||||
|
||||
$TestsPhoneHome = 0; # Should test report back to server?
|
||||
$GraphNameOverride = 'fx-linux-tbox';
|
||||
|
||||
# $results_server
|
||||
#----------------------------------------------------------------------------
|
||||
# Server on which test results will be accessible. This was originally tegu,
|
||||
# then became axolotl. Once we moved services from axolotl, it was time
|
||||
# to give this service its own hostname to make future transitions easier.
|
||||
# - cmp@mozilla.org
|
||||
#$results_server = "build-graphs.mozilla.org";
|
||||
|
||||
#$pageload_server = "spider"; # localhost
|
||||
$pageload_server = "pageload.build.mozilla.org";
|
||||
|
||||
|
||||
#
|
||||
# Timeouts, values are in seconds.
|
||||
#
|
||||
#$CVSCheckoutTimeout = 3600;
|
||||
#$CreateProfileTimeout = 45;
|
||||
#$RegxpcomTestTimeout = 120;
|
||||
|
||||
#$AliveTestTimeout = 45;
|
||||
#$ViewerTestTimeout = 45;
|
||||
#$EmbedTestTimeout = 45;
|
||||
#$BloatTestTimeout = 120; # seconds
|
||||
#$MailBloatTestTimeout = 120; # seconds
|
||||
#$JavaTestTimeout = 45;
|
||||
#$DomTestTimeout = 45; # seconds
|
||||
#$XpcomGlueTestTimeout = 15;
|
||||
#$CodesizeTestTimeout = 900; # seconds
|
||||
#$CodesizeTestType = "auto"; # {"auto"|"base"}
|
||||
#$LayoutPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$DHTMLPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$QATestTimeout = 1200; # entire test, seconds
|
||||
#$LayoutPerformanceTestPageTimeout = 30000; # each page, ms
|
||||
#$StartupPerformanceTestTimeout = 15; # seconds
|
||||
#$XULWindowOpenTestTimeout = 150; # seconds
|
||||
|
||||
|
||||
#$MozConfigFileName = 'mozconfig';
|
||||
|
||||
#$UseMozillaProfile = 1;
|
||||
#$MozProfileName = 'default';
|
||||
|
||||
#- Set these to what makes sense for your system
|
||||
#$Make = 'gmake'; # Must be GNU make
|
||||
#$MakeOverrides = '';
|
||||
#$mail = '/bin/mail';
|
||||
#$CVS = 'cvs -q';
|
||||
#$CVSCO = 'checkout -P';
|
||||
|
||||
# win32 usually doesn't have /bin/mail
|
||||
#$blat = 'c:/nstools/bin/blat';
|
||||
#$use_blat = 0;
|
||||
|
||||
# Set moz_cvsroot to something like:
|
||||
# :pserver:$ENV{USER}%netscape.com\@cvs.mozilla.org:/cvsroot
|
||||
# :pserver:anonymous\@cvs-mirror.mozilla.org:/cvsroot
|
||||
#
|
||||
# Note that win32 may not need \@, depends on ' or ".
|
||||
# :pserver:$ENV{USER}%netscape.com@cvs.mozilla.org:/cvsroot
|
||||
|
||||
#$moz_cvsroot = $ENV{CVSROOT};
|
||||
# CONFIG: $moz_cvsroot = '%mozillaCvsroot%';
|
||||
$moz_cvsroot = 'cltbld@cvs.mozilla.org:/cvsroot';
|
||||
|
||||
#- Set these proper values for your tinderbox server
|
||||
#$Tinderbox_server = 'tinderbox-daemon@tinderbox.mozilla.org';
|
||||
|
||||
# Allow for non-client builds, e.g. camino.
|
||||
#$moz_client_mk = 'client.mk';
|
||||
|
||||
#- Set if you want to build in a separate object tree
|
||||
$ObjDir = 'obj-fx-trunk';
|
||||
|
||||
# Extra build name, if needed.
|
||||
$BuildNameExtra = 'Release';
|
||||
|
||||
# User comment, eg. ip address for dhcp builds.
|
||||
# ex: $UserComment = "ip = 208.12.36.108";
|
||||
#$UserComment = 0;
|
||||
|
||||
#-
|
||||
#- The rest should not need to be changed
|
||||
#-
|
||||
|
||||
#- Minimum wait period from start of build to start of next build in minutes.
|
||||
#$BuildSleep = 10;
|
||||
|
||||
#- Until you get the script working. When it works,
|
||||
#- change to the tree you're actually building
|
||||
# CONFIG: $BuildTree = '%buildTree%';
|
||||
$BuildTree = 'MozillaRelease';
|
||||
|
||||
#$BuildName = '';
|
||||
# CONFIG: $BuildTag = '%productTag%_RELEASE';
|
||||
$BuildTag = 'FIREFOX_3_0_19_RELEASE';
|
||||
#$BuildConfigDir = 'mozilla/config';
|
||||
#$Topsrcdir = 'mozilla';
|
||||
|
||||
$BinaryName = 'firefox-bin';
|
||||
|
||||
#
|
||||
# For embedding app, use:
|
||||
#$EmbedBinaryName = 'TestGtkEmbed';
|
||||
#$EmbedDistDir = 'dist/bin'
|
||||
|
||||
|
||||
#$ShellOverride = ''; # Only used if the default shell is too stupid
|
||||
#$ConfigureArgs = '';
|
||||
#$ConfigureEnvArgs = '';
|
||||
#$Compiler = 'gcc';
|
||||
#$NSPRArgs = '';
|
||||
#$ShellOverride = '';
|
||||
|
||||
# Release build options
|
||||
$ReleaseBuild = 1;
|
||||
$shiptalkback = 0;
|
||||
$ReleaseToLatest = 0; # Push the release to latest-<milestone>?
|
||||
$ReleaseToDated = 1; # Push the release to YYYY-MM-DD-HH-<milestone>?
|
||||
$build_hour = 4;
|
||||
$package_creation_path = "/browser/installer";
|
||||
# needs setting for mac + talkback: $mac_bundle_path = "/browser/app";
|
||||
$ssh_version = "2";
|
||||
# CONFIG: $ssh_user = "%sshUser%";
|
||||
$ssh_user = "cltbld";
|
||||
# CONFIG: $ssh_server = "%sshServer%";
|
||||
$ssh_server = "stage-old.mozilla.org";
|
||||
$ftp_path = "/home/ftp/pub/firefox/nightly";
|
||||
$url_path = "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly";
|
||||
$tbox_ftp_path = "/home/ftp/pub/firefox/tinderbox-builds";
|
||||
$tbox_url_path = "http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds";
|
||||
# CONFIG: $milestone = "firefox%version%";
|
||||
$milestone = "firefox3.0.19";
|
||||
$notify_list = 'build-announce@mozilla.org';
|
||||
$stub_installer = 0;
|
||||
$sea_installer = 0;
|
||||
$archive = 1;
|
||||
$push_raw_xpis = 0;
|
||||
$update_pushinfo = 0;
|
||||
$update_package = 1;
|
||||
$update_product = "Firefox";
|
||||
$update_version = "trunk";
|
||||
$update_platform = "Linux_x86-gcc3";
|
||||
$update_hash = "sha1";
|
||||
$update_filehost = 'ftp.mozilla.org';
|
||||
$update_ver_file = 'browser/config/version.txt';
|
||||
$crashreporter_buildsymbols = 1;
|
||||
$crashreporter_pushsymbols = 1;
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_HOST'} = '%symbolServer%';
|
||||
$ENV{'SYMBOL_SERVER_HOST'} = 'dm-symbolpush01.mozilla.org';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_USER'} = '%symbolServerUser%';
|
||||
$ENV{'SYMBOL_SERVER_USER'} = 'ffxbld';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_PATH'} = '%symbolServerPath%';
|
||||
$ENV{'SYMBOL_SERVER_PATH'} = '/mnt/netapp/breakpad/symbols_ffx';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_SSH_KEY'} = '%symbolServerKey%';
|
||||
$ENV{'SYMBOL_SERVER_SSH_KEY'} = '/home/cltbld/.ssh/ffxbld_dsa';
|
||||
|
||||
# Reboot the OS at the end of build-and-test cycle. This is primarily
|
||||
# intended for Win9x, which can't last more than a few cycles before
|
||||
# locking up (and testing would be suspect even after a couple of cycles).
|
||||
# Right now, there is only code to force the reboot for Win9x, so even
|
||||
# setting this to 1, will not have an effect on other platforms. Setting
|
||||
# up win9x to automatically logon and begin running tinderbox is left
|
||||
# as an exercise to the reader.
|
||||
#$RebootSystem = 0;
|
||||
|
||||
# LogCompression specifies the type of compression used on the log file.
|
||||
# Valid options are 'gzip', and 'bzip2'. Please make sure the binaries
|
||||
# for 'gzip' or 'bzip2' are in the user's path before setting this
|
||||
# option.
|
||||
#$LogCompression = '';
|
||||
|
||||
# LogEncoding specifies the encoding format used for the logs. Valid
|
||||
# options are 'base64', and 'uuencode'. If $LogCompression is set above,
|
||||
# this needs to be set to 'base64' or 'uuencode' to ensure that the
|
||||
# binary data is transferred properly.
|
||||
#$LogEncoding = '';
|
||||
|
||||
# Prevent Extension Manager from spawning child processes during tests
|
||||
# - processes that tbox scripts cannot kill.
|
||||
#$ENV{NO_EM_RESTART} = '1';
|
||||
|
||||
# Do not build XForms
|
||||
$BuildXForms = 0;
|
||||
26
mozilla/tools/tinderbox-configs/firefox/macosx/mozconfig
Normal file
26
mozilla/tools/tinderbox-configs/firefox/macosx/mozconfig
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
## hostname: bm-xserve08.build.mozilla.org
|
||||
## uname: Darwin bm-xserve08.build.mozilla.org 8.8.4 Darwin Kernel Version 8.8.4: Sun Oct 29 15:26:54 PST 2006; root:xnu-792.16.4.obj~1/RELEASE_I386 i386 i386
|
||||
#
|
||||
|
||||
# symbols for breakpad
|
||||
export CFLAGS="-g -gfull"
|
||||
export CXXFLAGS="-g -gfull"
|
||||
|
||||
. $topsrcdir/build/macosx/universal/mozconfig
|
||||
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j1"
|
||||
mk_add_options MOZ_CO_MODULE="mozilla/tools/update-packaging"
|
||||
mk_add_options MOZ_CO_PROJECT="browser"
|
||||
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/../build/universal
|
||||
|
||||
ac_add_options --enable-application=browser
|
||||
ac_add_options --enable-update-channel=release
|
||||
# Don't add explicit optimize flags here, set them in configure.in, see bug 407794.
|
||||
ac_add_options --enable-optimize
|
||||
ac_add_options --disable-debug
|
||||
ac_add_options --disable-tests
|
||||
ac_add_options --enable-update-packaging
|
||||
|
||||
ac_add_options --enable-official-branding
|
||||
ac_add_app_options ppc --enable-prebinding
|
||||
267
mozilla/tools/tinderbox-configs/firefox/macosx/tinder-config.pl
Normal file
267
mozilla/tools/tinderbox-configs/firefox/macosx/tinder-config.pl
Normal file
@@ -0,0 +1,267 @@
|
||||
#
|
||||
## hostname: bm-xserve08.build.mozilla.org
|
||||
## uname: Darwin bm-xserve08.build.mozilla.org 8.8.4 Darwin Kernel Version 8.8.4: Sun Oct 29 15:26:54 PST 2006; root:xnu-792.16.4.obj~1/RELEASE_I386 i386 i386
|
||||
#
|
||||
|
||||
#- tinder-config.pl - Tinderbox configuration file.
|
||||
#- Uncomment the variables you need to set.
|
||||
#- The default values are the same as the commented variables.
|
||||
|
||||
$ENV{NO_EM_RESTART} = "1";
|
||||
$ENV{DYLD_NO_FIX_PREBINDING} = "1";
|
||||
$ENV{LD_PREBIND_ALLOW_OVERLAP} = "1";
|
||||
$ENV{CVS_RSH} = "ssh";
|
||||
|
||||
$MacUniversalBinary = 1;
|
||||
|
||||
# $ENV{MOZ_PACKAGE_MSI}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: 0
|
||||
# Values: 0 | 1
|
||||
# Purpose: Controls whether a MSI package is made.
|
||||
# Requires: Windows and a local MakeMSI installation.
|
||||
#$ENV{MOZ_PACKAGE_MSI} = 0;
|
||||
|
||||
# $ENV{MOZ_SYMBOLS_TRANSFER_TYPE}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: scp
|
||||
# Values: scp | rsync
|
||||
# Purpose: Use scp or rsync to transfer symbols to the Talkback server.
|
||||
# Requires: The selected type requires the command be available both locally
|
||||
# and on the Talkback server.
|
||||
#$ENV{MOZ_SYMBOLS_TRANSFER_TYPE} = "scp";
|
||||
|
||||
#- PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
|
||||
$BuildAdministrator = 'build@mozilla.org';
|
||||
#$BuildAdministrator = "$ENV{USER}\@$ENV{HOST}";
|
||||
#$BuildAdministrator = ($ENV{USER} || "cltbld") . "\@" . ($ENV{HOST} || "dhcp");
|
||||
|
||||
#- You'll need to change these to suit your machine's needs
|
||||
#$DisplayServer = ':0.0';
|
||||
|
||||
#- Default values of command-line opts
|
||||
#-
|
||||
#$BuildDepend = 1; # Depend or Clobber
|
||||
#$BuildDebug = 0; # Debug or Opt (Darwin)
|
||||
#$ReportStatus = 1; # Send results to server, or not
|
||||
#$ReportFinalStatus = 1; # Finer control over $ReportStatus.
|
||||
$UseTimeStamp = 0; # Use the CVS 'pull-by-timestamp' option, or not
|
||||
#$BuildOnce = 0; # Build once, don't send results to server
|
||||
#$TestOnly = 0; # Only run tests, don't pull/build
|
||||
#$BuildEmbed = 0; # After building seamonkey, go build embed app.
|
||||
#$SkipMozilla = 0; # Use to debug post-mozilla.pl scripts.
|
||||
#$BuildLocales = 0; # Do l10n packaging?
|
||||
|
||||
# Tests
|
||||
$CleanProfile = 1;
|
||||
#$ResetHomeDirForTests = 1;
|
||||
$ProductName = 'Firefox';
|
||||
$VendorName = "Mozilla";
|
||||
|
||||
$RunMozillaTests = 1; # Allow turning off of all tests if needed.
|
||||
$RegxpcomTest = 1;
|
||||
$AliveTest = 1;
|
||||
#$JavaTest = 0;
|
||||
#$ViewerTest = 0;
|
||||
#$BloatTest = 0; # warren memory bloat test
|
||||
#$BloatTest2 = 0; # dbaron memory bloat test, require tracemalloc
|
||||
#$DomToTextConversionTest = 0;
|
||||
#$XpcomGlueTest = 0;
|
||||
$CodesizeTest = 0; # Z, require mozilla/tools/codesighs
|
||||
$EmbedCodesizeTest = 0; # mZ, require mozilla/tools/codesigns
|
||||
#$MailBloatTest = 0;
|
||||
#$EmbedTest = 0; # Assumes you wanted $BuildEmbed=1
|
||||
$LayoutPerformanceTest = 0; # Tp
|
||||
$LayoutPerformanceLocalTest = 0; # Tp2
|
||||
$DHTMLPerformanceTest = 0; # Tdhtml
|
||||
#$QATest = 0;
|
||||
$XULWindowOpenTest = 0; # Txul
|
||||
$StartupPerformanceTest = 0; # Ts
|
||||
|
||||
$TestsPhoneHome = 0; # Should test report back to server?
|
||||
|
||||
$GraphNameOverride = 'xserve08.build.mozilla.org_Fx-Trunk';
|
||||
|
||||
# $results_server
|
||||
#----------------------------------------------------------------------------
|
||||
# Server on which test results will be accessible. This was originally tegu,
|
||||
# then became axolotl. Once we moved services from axolotl, it was time
|
||||
# to give this service its own hostname to make future transitions easier.
|
||||
# - cmp@mozilla.org
|
||||
#$results_server = "build-graphs.mozilla.org";
|
||||
|
||||
#$pageload_server = "spider"; # localhost
|
||||
$pageload_server = "pageload.build.mozilla.org"; # localhost
|
||||
|
||||
#
|
||||
# Timeouts, values are in seconds.
|
||||
#
|
||||
#$CVSCheckoutTimeout = 3600;
|
||||
#$CreateProfileTimeout = 45;
|
||||
#$RegxpcomTestTimeout = 120;
|
||||
|
||||
$AliveTestTimeout = 10;
|
||||
#$ViewerTestTimeout = 45;
|
||||
#$EmbedTestTimeout = 45;
|
||||
#$BloatTestTimeout = 120; # seconds
|
||||
#$MailBloatTestTimeout = 120; # seconds
|
||||
#$JavaTestTimeout = 45;
|
||||
#$DomTestTimeout = 45; # seconds
|
||||
#$XpcomGlueTestTimeout = 15;
|
||||
#$CodesizeTestTimeout = 900; # seconds
|
||||
#$CodesizeTestType = "auto"; # {"auto"|"base"}
|
||||
$LayoutPerformanceTestTimeout = 300; # entire test, seconds
|
||||
$LayoutPerformanceLocalTestTimeout = 180; # entire test, seconds
|
||||
$DHTMLPerformanceTestTimeout = 180; # entire test, seconds
|
||||
#$QATestTimeout = 1200; # entire test, seconds
|
||||
#$LayoutPerformanceTestPageTimeout = 30000; # each page, ms
|
||||
#$StartupPerformanceTestTimeout = 15; # seconds
|
||||
#$XULWindowOpenTestTimeout = 150; # seconds
|
||||
|
||||
|
||||
#$MozConfigFileName = 'mozconfig';
|
||||
|
||||
#$UseMozillaProfile = 1;
|
||||
#$MozProfileName = 'default';
|
||||
|
||||
#- Set these to what makes sense for your system
|
||||
#$Make = 'gmake'; # Must be GNU make
|
||||
#$MakeOverrides = '';
|
||||
#$mail = '/bin/mail';
|
||||
#$CVS = 'cvs -q';
|
||||
#$CVSCO = 'checkout -P';
|
||||
|
||||
# win32 usually doesn't have /bin/mail
|
||||
#$blat = 'c:/nstools/bin/blat';
|
||||
#$use_blat = 0;
|
||||
|
||||
# Set moz_cvsroot to something like:
|
||||
# :pserver:$ENV{USER}%netscape.com\@cvs.mozilla.org:/cvsroot
|
||||
# :pserver:anonymous\@cvs-mirror.mozilla.org:/cvsroot
|
||||
#
|
||||
# Note that win32 may not need \@, depends on ' or ".
|
||||
# :pserver:$ENV{USER}%netscape.com@cvs.mozilla.org:/cvsroot
|
||||
|
||||
# CONFIG: $moz_cvsroot = '%mozillaCvsroot%';
|
||||
$moz_cvsroot = 'cltbld@cvs.mozilla.org:/cvsroot';
|
||||
|
||||
#- Set these proper values for your tinderbox server
|
||||
#$Tinderbox_server = 'tinderbox-daemon@tinderbox.mozilla.org';
|
||||
|
||||
# Allow for non-client builds, e.g. camino.
|
||||
#$moz_client_mk = 'client.mk';
|
||||
|
||||
#- Set if you want to build in a separate object tree
|
||||
$ObjDir = '../build/universal';
|
||||
|
||||
# Extra build name, if needed.
|
||||
$BuildNameExtra = 'Release';
|
||||
|
||||
# User comment, eg. ip address for dhcp builds.
|
||||
# ex: $UserComment = "ip = 208.12.36.108";
|
||||
#$UserComment = 0;
|
||||
|
||||
#-
|
||||
#- The rest should not need to be changed
|
||||
#-
|
||||
|
||||
#- Minimum wait period from start of build to start of next build in minutes.
|
||||
#$BuildSleep = 10;
|
||||
|
||||
#- Until you get the script working. When it works,
|
||||
#- change to the tree you're actually building
|
||||
# CONFIG: $BuildTree = '%buildTree%';
|
||||
$BuildTree = 'MozillaRelease';
|
||||
|
||||
#$BuildName = '';
|
||||
# CONFIG: $BuildTag = '%productTag%_RELEASE';
|
||||
$BuildTag = 'FIREFOX_3_0_19_RELEASE';
|
||||
#$BuildConfigDir = 'mozilla/config';
|
||||
#$Topsrcdir = 'mozilla';
|
||||
|
||||
$BinaryName = 'firefox-bin';
|
||||
|
||||
#
|
||||
# For embedding app, use:
|
||||
#$EmbedBinaryName = 'TestGtkEmbed';
|
||||
#$EmbedDistDir = 'dist/bin'
|
||||
|
||||
|
||||
#$ShellOverride = ''; # Only used if the default shell is too stupid
|
||||
#$ConfigureArgs = '';
|
||||
#$ConfigureEnvArgs = '';
|
||||
#$Compiler = 'gcc';
|
||||
#$NSPRArgs = '';
|
||||
#$ShellOverride = '';
|
||||
|
||||
# Release build options
|
||||
$ReleaseBuild = 1;
|
||||
$shiptalkback = 0;
|
||||
$ReleaseToLatest = 0; # Push the release to latest-<milestone>?
|
||||
$ReleaseToDated = 1; # Push the release to YYYY-MM-DD-HH-<milestone>?
|
||||
$build_hour = "4";
|
||||
$package_creation_path = "/browser/installer";
|
||||
# needs setting for mac + talkback: $mac_bundle_path = "/browser/app";
|
||||
$mac_bundle_path = "/browser/app";
|
||||
$ssh_version = "2";
|
||||
# CONFIG: $ssh_user = "%sshUser%";
|
||||
$ssh_user = "cltbld";
|
||||
# CONFIG: $ssh_server = "%sshServer%";
|
||||
$ssh_server = "stage-old.mozilla.org";
|
||||
$ftp_path = "/home/ftp/pub/firefox/nightly";
|
||||
$url_path = "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly";
|
||||
$tbox_ftp_path = "/home/ftp/pub/firefox/tinderbox-builds";
|
||||
$tbox_url_path = "http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds";
|
||||
# CONFIG: $milestone = 'firefox%version%';
|
||||
$milestone = 'firefox3.0.19';
|
||||
$notify_list = "build-announce\@mozilla.org";
|
||||
$stub_installer = 0;
|
||||
$sea_installer = 0;
|
||||
$archive = 1;
|
||||
$push_raw_xpis = 0;
|
||||
$update_package = 1;
|
||||
$update_product = "Firefox";
|
||||
$update_version = "trunk";
|
||||
$update_platform = "Darwin_Universal-gcc3";
|
||||
$update_hash = "sha1";
|
||||
$update_filehost = "ftp.mozilla.org";
|
||||
$update_ver_file = 'browser/config/version.txt';
|
||||
$update_pushinfo = 0;
|
||||
$crashreporter_buildsymbols = 1;
|
||||
$crashreporter_pushsymbols = 1;
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_HOST'} = '%symbolServer%';
|
||||
$ENV{'SYMBOL_SERVER_HOST'} = 'dm-symbolpush01.mozilla.org';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_USER'} = '%symbolServerUser%';
|
||||
$ENV{'SYMBOL_SERVER_USER'} = 'ffxbld';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_PATH'} = '%symbolServerPath%';
|
||||
$ENV{'SYMBOL_SERVER_PATH'} = '/mnt/netapp/breakpad/symbols_ffx';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_SSH_KEY'} = '%symbolServerKey%';
|
||||
$ENV{'SYMBOL_SERVER_SSH_KEY'} = '/Users/cltbld/.ssh/ffxbld_dsa';
|
||||
|
||||
# Reboot the OS at the end of build-and-test cycle. This is primarily
|
||||
# intended for Win9x, which can't last more than a few cycles before
|
||||
# locking up (and testing would be suspect even after a couple of cycles).
|
||||
# Right now, there is only code to force the reboot for Win9x, so even
|
||||
# setting this to 1, will not have an effect on other platforms. Setting
|
||||
# up win9x to automatically logon and begin running tinderbox is left
|
||||
# as an exercise to the reader.
|
||||
#$RebootSystem = 0;
|
||||
|
||||
# LogCompression specifies the type of compression used on the log file.
|
||||
# Valid options are 'gzip', and 'bzip2'. Please make sure the binaries
|
||||
# for 'gzip' or 'bzip2' are in the user's path before setting this
|
||||
# option.
|
||||
#$LogCompression = '';
|
||||
|
||||
# LogEncoding specifies the encoding format used for the logs. Valid
|
||||
# options are 'base64', and 'uuencode'. If $LogCompression is set above,
|
||||
# this needs to be set to 'base64' or 'uuencode' to ensure that the
|
||||
# binary data is transferred properly.
|
||||
#$LogEncoding = '';
|
||||
|
||||
# Prevent Extension Manager from spawning child processes during tests
|
||||
# - processes that tbox scripts cannot kill.
|
||||
#$ENV{NO_EM_RESTART} = '1';
|
||||
|
||||
# Do not build XForms
|
||||
$BuildXForms = 0;
|
||||
22
mozilla/tools/tinderbox-configs/firefox/win32/mozconfig
Normal file
22
mozilla/tools/tinderbox-configs/firefox/win32/mozconfig
Normal file
@@ -0,0 +1,22 @@
|
||||
#
|
||||
## hostname: fx-win32-tbox
|
||||
## uname: MINGW32_NT-5.2 FX-WIN32-TBOX 1.0.11(0.46/3/2) 2007-01-12 12:05 i686 Msys
|
||||
#
|
||||
export CFLAGS="-GL -wd4624 -wd4952"
|
||||
export CXXFLAGS="-GL -wd4624 -wd4952"
|
||||
export LDFLAGS="-LTCG"
|
||||
|
||||
mk_add_options MOZ_CO_PROJECT=browser
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j1"
|
||||
mk_add_options MOZ_CO_MODULE="mozilla/tools/update-packaging"
|
||||
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) $(MOZ_OBJDIR)/_profile/pgo/profileserver.py'
|
||||
|
||||
ac_add_options --enable-application=browser
|
||||
ac_add_options --enable-update-channel=release
|
||||
ac_add_options --enable-optimize
|
||||
ac_add_options --disable-debug
|
||||
ac_add_options --disable-tests
|
||||
ac_add_options --enable-update-packaging
|
||||
ac_add_options --enable-official-branding
|
||||
ac_add_options --enable-jemalloc
|
||||
ac_add_options --with-crashreporter-enable-percent=10
|
||||
267
mozilla/tools/tinderbox-configs/firefox/win32/tinder-config.pl
Normal file
267
mozilla/tools/tinderbox-configs/firefox/win32/tinder-config.pl
Normal file
@@ -0,0 +1,267 @@
|
||||
#
|
||||
## hostname: fx-win32-tbox
|
||||
## uname: MINGW32_NT-5.2 FX-WIN32-TBOX 1.0.11(0.46/3/2) 2007-01-12 12:05 i686 Msys
|
||||
#
|
||||
|
||||
#- tinder-config.pl - Tinderbox configuration file.
|
||||
#- Uncomment the variables you need to set.
|
||||
#- The default values are the same as the commented variables.
|
||||
|
||||
$ENV{NO_EM_RESTART} = '1';
|
||||
$ENV{CVS_RSH} = "ssh";
|
||||
$ENV{MOZ_CRASHREPORTER_NO_REPORT} = '1';
|
||||
# Both these two variables are for source server support
|
||||
$ENV{PDBSTR_PATH} = 'C:\\Program Files\\Debugging Tools for Windows\\sdk\\srcsrv\\pdbstr.exe';
|
||||
$ENV{SRCSRV_ROOT} = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot';
|
||||
|
||||
# $ENV{MOZ_PACKAGE_MSI}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: 0
|
||||
# Values: 0 | 1
|
||||
# Purpose: Controls whether a MSI package is made.
|
||||
# Requires: Windows and a local MakeMSI installation.
|
||||
#$ENV{MOZ_PACKAGE_MSI} = 0;
|
||||
|
||||
# $ENV{MOZ_SYMBOLS_TRANSFER_TYPE}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: scp
|
||||
# Values: scp | rsync
|
||||
# Purpose: Use scp or rsync to transfer symbols to the Talkback server.
|
||||
# Requires: The selected type requires the command be available both locally
|
||||
# and on the Talkback server.
|
||||
#$ENV{MOZ_SYMBOLS_TRANSFER_TYPE} = "scp";
|
||||
|
||||
#- PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
|
||||
$BuildAdministrator = 'build@mozilla.org';
|
||||
#$BuildAdministrator = "$ENV{USER}\@$ENV{HOST}";
|
||||
#$BuildAdministrator = ($ENV{USER} || "cltbld") . "\@" . ($ENV{HOST} || "dhcp");
|
||||
|
||||
#- You'll need to change these to suit your machine's needs
|
||||
#$DisplayServer = ':0.0';
|
||||
|
||||
#- Default values of command-line opts
|
||||
#-
|
||||
#$BuildDepend = 1; # Depend or Clobber
|
||||
#$BuildDebug = 0; # Debug or Opt (Darwin)
|
||||
#$ReportStatus = 1; # Send results to server, or not
|
||||
#$ReportFinalStatus = 1; # Finer control over $ReportStatus.
|
||||
$UseTimeStamp = 0; # Use the CVS 'pull-by-timestamp' option, or not
|
||||
#$BuildOnce = 0; # Build once, don't send results to server
|
||||
#$TestOnly = 0; # Only run tests, don't pull/build
|
||||
#$BuildEmbed = 0; # After building seamonkey, go build embed app.
|
||||
#$SkipMozilla = 0; # Use to debug post-mozilla.pl scripts.
|
||||
#$BuildLocales = 0; # Do l10n packaging?
|
||||
|
||||
# Tests
|
||||
$CleanProfile = 1;
|
||||
#$ResetHomeDirForTests = 1;
|
||||
$ProductName = "Firefox";
|
||||
$VendorName = "Mozilla";
|
||||
|
||||
$RunMozillaTests = 1; # Allow turning off of all tests if needed.
|
||||
$RegxpcomTest = 1;
|
||||
$AliveTest = 1;
|
||||
$JavaTest = 0;
|
||||
$ViewerTest = 0;
|
||||
$BloatTest = 0; # warren memory bloat test
|
||||
$BloatTest2 = 0; # dbaron memory bloat test, require tracemalloc
|
||||
$DomToTextConversionTest = 0;
|
||||
$XpcomGlueTest = 0;
|
||||
$CodesizeTest = 0; # Z, require mozilla/tools/codesighs
|
||||
$EmbedCodesizeTest = 0; # mZ, require mozilla/tools/codesigns
|
||||
$MailBloatTest = 0;
|
||||
$EmbedTest = 0; # Assumes you wanted $BuildEmbed=1
|
||||
$LayoutPerformanceTest = 0; # Tp
|
||||
$DHTMLPerformanceTest = 0; # Tdhtml
|
||||
$QATest = 0;
|
||||
$XULWindowOpenTest = 0; # Txul
|
||||
$StartupPerformanceTest = 0; # Ts
|
||||
$NeckoUnitTest = 0;
|
||||
$RenderPerformanceTest = 0; # Tgfx
|
||||
|
||||
$TestsPhoneHome = 0; # Should test report back to server?
|
||||
$GraphNameOverride = 'fx-win32-tbox';
|
||||
|
||||
# $results_server
|
||||
#----------------------------------------------------------------------------
|
||||
# Server on which test results will be accessible. This was originally tegu,
|
||||
# then became axolotl. Once we moved services from axolotl, it was time
|
||||
# to give this service its own hostname to make future transitions easier.
|
||||
# - cmp@mozilla.org
|
||||
#$results_server = "build-graphs.mozilla.org";
|
||||
|
||||
$pageload_server = "pageload.build.mozilla.org"; # localhost
|
||||
|
||||
#
|
||||
# Timeouts, values are in seconds.
|
||||
#
|
||||
#$CVSCheckoutTimeout = 3600;
|
||||
#$CreateProfileTimeout = 45;
|
||||
#$RegxpcomTestTimeout = 120;
|
||||
|
||||
#$AliveTestTimeout = 30;
|
||||
#$ViewerTestTimeout = 45;
|
||||
#$EmbedTestTimeout = 45;
|
||||
#$BloatTestTimeout = 120; # seconds
|
||||
#$MailBloatTestTimeout = 120; # seconds
|
||||
#$JavaTestTimeout = 45;
|
||||
#$DomTestTimeout = 45; # seconds
|
||||
#$XpcomGlueTestTimeout = 15;
|
||||
#$CodesizeTestTimeout = 900; # seconds
|
||||
#$CodesizeTestType = "auto"; # {"auto"|"base"}
|
||||
$LayoutPerformanceTestTimeout = 800; # entire test, seconds
|
||||
#$DHTMLPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$QATestTimeout = 1200; # entire test, seconds
|
||||
#$LayoutPerformanceTestPageTimeout = 30000; # each page, ms
|
||||
#$StartupPerformanceTestTimeout = 20; # seconds
|
||||
#$XULWindowOpenTestTimeout = 90; # seconds
|
||||
#$NeckoUnitTestTimeout = 30; # seconds
|
||||
$RenderPerformanceTestTimeout = 1800; # seconds
|
||||
|
||||
#$MozConfigFileName = 'mozconfig';
|
||||
|
||||
#$UseMozillaProfile = 1;
|
||||
#$MozProfileName = 'default';
|
||||
|
||||
#- Set these to what makes sense for your system
|
||||
$Make = 'make'; # Must be GNU make
|
||||
#$MakeOverrides = '';
|
||||
#$mail = '/bin/mail';
|
||||
#$CVS = 'cvs -q';
|
||||
#$CVSCO = 'checkout -P';
|
||||
|
||||
# win32 usually doesn't have /bin/mail
|
||||
$blat = '/d/mozilla-build/blat261/full/blat';
|
||||
#$use_blat = 1;
|
||||
|
||||
# Set moz_cvsroot to something like:
|
||||
# :pserver:$ENV{USER}%netscape.com\@cvs.mozilla.org:/cvsroot
|
||||
# :pserver:anonymous\@cvs-mirror.mozilla.org:/cvsroot
|
||||
#
|
||||
# Note that win32 may not need \@, depends on ' or ".
|
||||
# :pserver:$ENV{USER}%netscape.com@cvs.mozilla.org:/cvsroot
|
||||
|
||||
# CONFIG: $moz_cvsroot = '%mozillaCvsroot%';
|
||||
$moz_cvsroot = 'cltbld@cvs.mozilla.org:/cvsroot';
|
||||
|
||||
#- Set these proper values for your tinderbox server
|
||||
#$Tinderbox_server = 'tinderbox-daemon@tinderbox.mozilla.org';
|
||||
|
||||
# Allow for non-client builds, e.g. camino.
|
||||
#$moz_client_mk = 'client.mk';
|
||||
|
||||
#- Set if you want to build in a separate object tree
|
||||
$ObjDir = 'obj-fx-trunk';
|
||||
|
||||
# Extra build name, if needed.
|
||||
$BuildNameExtra = 'Release';
|
||||
|
||||
# User comment, eg. ip address for dhcp builds.
|
||||
# ex: $UserComment = "ip = 208.12.36.108";
|
||||
#$UserComment = 0;
|
||||
|
||||
#-
|
||||
#- The rest should not need to be changed
|
||||
#-
|
||||
|
||||
#- Minimum wait period from start of build to start of next build in minutes.
|
||||
#$BuildSleep = 10;
|
||||
|
||||
#- Until you get the script working. When it works,
|
||||
#- change to the tree you're actually building
|
||||
#$BuildTree = 'MozillaTest';
|
||||
# CONFIG: $BuildTree = '%buildTree%';
|
||||
$BuildTree = 'MozillaRelease';
|
||||
|
||||
#$BuildName = '';
|
||||
# CONFIG: $BuildTag = '%productTag%_RELEASE';
|
||||
$BuildTag = 'FIREFOX_3_0_19_RELEASE';
|
||||
#$BuildConfigDir = 'mozilla/config';
|
||||
#$Topsrcdir = 'mozilla';
|
||||
|
||||
$BinaryName = 'firefox.exe';
|
||||
|
||||
#
|
||||
# For embedding app, use:
|
||||
#$EmbedBinaryName = 'TestGtkEmbed';
|
||||
#$EmbedDistDir = 'dist/bin'
|
||||
|
||||
|
||||
#$ShellOverride = ''; # Only used if the default shell is too stupid
|
||||
#$ConfigureArgs = '';
|
||||
#$ConfigureEnvArgs = '';
|
||||
#$Compiler = 'gcc';
|
||||
#$NSPRArgs = '';
|
||||
#$ShellOverride = '';
|
||||
|
||||
$ProfiledBuild = 1;
|
||||
# Release build options
|
||||
$ReleaseBuild = 1;
|
||||
$shiptalkback = 0;
|
||||
$ReleaseToLatest = 0; # Push the release to latest-<milestone>?
|
||||
$ReleaseToDated = 1; # Push the release to YYYY-MM-DD-HH-<milestone>?
|
||||
$build_hour = "4";
|
||||
$package_creation_path = "/browser/installer";
|
||||
# needs setting for mac + talkback: $mac_bundle_path = "/browser/app";
|
||||
$ssh_version = "2";
|
||||
# CONFIG: $ssh_user = "%sshUser%";
|
||||
$ssh_user = "cltbld";
|
||||
# CONFIG: $ssh_server = "%sshServer%";
|
||||
$ssh_server = "stage-old.mozilla.org";
|
||||
$ftp_path = "/home/ftp/pub/firefox/nightly";
|
||||
$url_path = "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly";
|
||||
$tbox_ftp_path = "/home/ftp/pub/firefox/tinderbox-builds";
|
||||
$tbox_url_path = "http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds";
|
||||
# CONFIG: $milestone = 'firefox%version%';
|
||||
$milestone = 'firefox3.0.19';
|
||||
$notify_list = 'build-announce@mozilla.org';
|
||||
$stub_installer = 0;
|
||||
$sea_installer = 1;
|
||||
$archive = 1;
|
||||
$push_raw_xpis = 0;
|
||||
$update_package = 1;
|
||||
$update_product = "Firefox";
|
||||
$update_version = "trunk";
|
||||
$update_platform = "WINNT_x86-msvc";
|
||||
$update_hash = "sha1";
|
||||
$update_filehost = "ftp.mozilla.org";
|
||||
$update_ver_file = 'browser/config/version.txt';
|
||||
$update_pushinfo = 0;
|
||||
$crashreporter_buildsymbols = 1;
|
||||
$crashreporter_pushsymbols = 1;
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_HOST'} = '%symbolServer%';
|
||||
$ENV{'SYMBOL_SERVER_HOST'} = 'dm-symbolpush01.mozilla.org';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_USER'} = '%symbolServerUser%';
|
||||
$ENV{'SYMBOL_SERVER_USER'} = 'ffxbld';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_PATH'} = '%symbolServerPath%';
|
||||
$ENV{'SYMBOL_SERVER_PATH'} = '/mnt/netapp/breakpad/symbols_ffx';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_SSH_KEY'} = '%symbolServerKey%';
|
||||
$ENV{'SYMBOL_SERVER_SSH_KEY'} = '/c/Documents and Settings/cltbld/.ssh/ffxbld_dsa';
|
||||
|
||||
# Reboot the OS at the end of build-and-test cycle. This is primarily
|
||||
# intended for Win9x, which can't last more than a few cycles before
|
||||
# locking up (and testing would be suspect even after a couple of cycles).
|
||||
# Right now, there is only code to force the reboot for Win9x, so even
|
||||
# setting this to 1, will not have an effect on other platforms. Setting
|
||||
# up win9x to automatically logon and begin running tinderbox is left
|
||||
# as an exercise to the reader.
|
||||
#$RebootSystem = 0;
|
||||
|
||||
# LogCompression specifies the type of compression used on the log file.
|
||||
# Valid options are 'gzip', and 'bzip2'. Please make sure the binaries
|
||||
# for 'gzip' or 'bzip2' are in the user's path before setting this
|
||||
# option.
|
||||
#$LogCompression = '';
|
||||
|
||||
# LogEncoding specifies the encoding format used for the logs. Valid
|
||||
# options are 'base64', and 'uuencode'. If $LogCompression is set above,
|
||||
# this needs to be set to 'base64' or 'uuencode' to ensure that the
|
||||
# binary data is transferred properly.
|
||||
#$LogEncoding = '';
|
||||
|
||||
# Prevent Extension Manager from spawning child processes during tests
|
||||
# - processes that tbox scripts cannot kill.
|
||||
#$ENV{NO_EM_RESTART} = '1';
|
||||
|
||||
# Do not build XForms
|
||||
$BuildXForms = 0;
|
||||
@@ -0,0 +1 @@
|
||||
Clobbering to force nightly due to nightly bustage from bug 428672.
|
||||
25
mozilla/tools/tinderbox-configs/thunderbird/linux/mozconfig
Normal file
25
mozilla/tools/tinderbox-configs/thunderbird/linux/mozconfig
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
## hostname: tb-linux-tbox
|
||||
## uname: Linux tb-linux-tbox.build.mozilla.org 2.6.18-8.el5 #1 SMP Thu Mar 15 19:57:35 EDT 2007 i686 athlon i386 GNU/Linux
|
||||
#
|
||||
|
||||
# symbols for breakpad
|
||||
export CFLAGS="-gstabs+"
|
||||
export CXXFLAGS="-gstabs+"
|
||||
|
||||
mk_add_options MOZ_CO_PROJECT=mail
|
||||
mk_add_options MOZ_MAKE_FLAGS=-j1
|
||||
mk_add_options MOZ_CO_MODULE="mozilla/tools/update-packaging"
|
||||
|
||||
ac_add_options --enable-application=mail
|
||||
ac_add_options --enable-update-channel=beta
|
||||
ac_add_options --disable-debug
|
||||
ac_add_options --enable-update-packaging
|
||||
# Add explicit optimize flags in configure.in, not here - see bug 407794
|
||||
ac_add_options --enable-optimize
|
||||
ac_add_options --disable-tests
|
||||
ac_add_options --disable-shared
|
||||
ac_add_options --enable-static
|
||||
|
||||
CC=/tools/gcc-4.1.1/bin/gcc
|
||||
CXX=/tools/gcc-4.1.1/bin/g++
|
||||
@@ -0,0 +1,225 @@
|
||||
#
|
||||
## hostname: tb-linux-tbox
|
||||
## uname: Linux tbnewref-linux-tbox.build.mozilla.org 2.6.18-8.el5 #1 SMP Thu Mar 15 19:57:35 EDT 2007 i686 athlon i386 GNU/Linux
|
||||
#
|
||||
|
||||
#- tinder-config.pl - Tinderbox configuration file.
|
||||
#- Uncomment the variables you need to set.
|
||||
#- The default values are the same as the commented variables.
|
||||
|
||||
$ENV{CVS_RSH} = "ssh";
|
||||
$ENV{MOZ_CRASHREPORTER_NO_REPORT} = '1';
|
||||
|
||||
#- PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
|
||||
#$BuildAdministrator = "$ENV{USER}\@$ENV{HOST}";
|
||||
#$BuildAdministrator = ($ENV{USER} || "cltbld") . "\@" . ($ENV{HOST} || "dhcp");
|
||||
|
||||
#- You'll need to change these to suit your machine's needs
|
||||
#$DisplayServer = ':0.0';
|
||||
|
||||
#- Default values of command-line opts
|
||||
#-
|
||||
$BuildDepend = 0; # Depend or Clobber
|
||||
#$BuildDebug = 0; # Debug or Opt (Darwin)
|
||||
#$ReportStatus = 1; # Send results to server, or not
|
||||
#$ReportFinalStatus = 1; # Finer control over $ReportStatus.
|
||||
$UseTimeStamp = 0; # Use the CVS 'pull-by-timestamp' option, or not
|
||||
#$BuildOnce = 0; # Build once, don't send results to server
|
||||
#$TestOnly = 0; # Only run tests, don't pull/build
|
||||
#$BuildEmbed = 0; # After building seamonkey, go build embed app.
|
||||
#$SkipMozilla = 0; # Use to debug post-mozilla.pl scripts.
|
||||
|
||||
# Tests
|
||||
$CleanProfile = 1;
|
||||
#$ResetHomeDirForTests = 1;
|
||||
$ProductName = "Thunderbird";
|
||||
#$VendorName = "";
|
||||
|
||||
$RunMozillaTests = 1; # Allow turning off of all tests if needed.
|
||||
#$RegxpcomTest = 1;
|
||||
#$AliveTest = 1;
|
||||
#$JavaTest = 0;
|
||||
#$ViewerTest = 0;
|
||||
#$BloatTest = 0; # warren memory bloat test
|
||||
#$BloatTest2 = 0; # dbaron memory bloat test, require tracemalloc
|
||||
#$DomToTextConversionTest = 0;
|
||||
#$XpcomGlueTest = 0;
|
||||
$CodesizeTest = 0; # Z, require mozilla/tools/codesighs
|
||||
#$EmbedCodesizeTest = 0; # mZ, require mozilla/tools/codesigns
|
||||
#$MailBloatTest = 0;
|
||||
#$EmbedTest = 0; # Assumes you wanted $BuildEmbed=1
|
||||
#$LayoutPerformanceTest = 0; # Tp
|
||||
#$QATest = 0;
|
||||
#$XULWindowOpenTest = 0; # Txul
|
||||
#$StartupPerformanceTest = 0; # Ts
|
||||
|
||||
$TestsPhoneHome = 0; # Should test report back to server?
|
||||
#$results_server = "axolotl.mozilla.org"; # was tegu
|
||||
#$pageload_server = "spider"; # localhost
|
||||
|
||||
#
|
||||
# Timeouts, values are in seconds.
|
||||
#
|
||||
#$CVSCheckoutTimeout = 3600;
|
||||
#$CreateProfileTimeout = 45;
|
||||
#$RegxpcomTestTimeout = 15;
|
||||
|
||||
#$AliveTestTimeout = 45;
|
||||
#$ViewerTestTimeout = 45;
|
||||
#$EmbedTestTimeout = 45;
|
||||
#$BloatTestTimeout = 120; # seconds
|
||||
#$MailBloatTestTimeout = 120; # seconds
|
||||
#$JavaTestTimeout = 45;
|
||||
#$DomTestTimeout = 45; # seconds
|
||||
#$XpcomGlueTestTimeout = 15;
|
||||
#$CodesizeTestTimeout = 900; # seconds
|
||||
#$CodesizeTestType = "auto"; # {"auto"|"base"}
|
||||
#$LayoutPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$QATestTimeout = 1200; # entire test, seconds
|
||||
#$LayoutPerformanceTestPageTimeout = 30000; # each page, ms
|
||||
#$StartupPerformanceTestTimeout = 60; # seconds
|
||||
#$XULWindowOpenTestTimeout = 150; # seconds
|
||||
|
||||
|
||||
#$MozConfigFileName = 'mozconfig';
|
||||
|
||||
#$UseMozillaProfile = 1;
|
||||
#$MozProfileName = 'default';
|
||||
|
||||
#- Set these to what makes sense for your system
|
||||
#$Make = 'gmake'; # Must be GNU make
|
||||
#$MakeOverrides = '';
|
||||
#$mail = '/bin/mail';
|
||||
#$CVS = 'cvs -q';
|
||||
#$CVSCO = 'checkout -P';
|
||||
|
||||
# win32 usually doesn't have /bin/mail
|
||||
#$blat = 'c:/nstools/bin/blat';
|
||||
#$use_blat = 0;
|
||||
|
||||
# Set moz_cvsroot to something like:
|
||||
# :pserver:$ENV{USER}%netscape.com\@cvs.mozilla.org:/cvsroot
|
||||
# :pserver:anonymous\@cvs-mirror.mozilla.org:/cvsroot
|
||||
#
|
||||
# Note that win32 may not need \@, depends on ' or ".
|
||||
# :pserver:$ENV{USER}%netscape.com@cvs.mozilla.org:/cvsroot
|
||||
|
||||
# CONFIG: $moz_cvsroot = '%mozillaCvsroot%';
|
||||
$moz_cvsroot = 'cltbld@cvs.mozilla.org:/cvsroot';
|
||||
|
||||
#- Set these proper values for your tinderbox server
|
||||
#$Tinderbox_server = 'tinderbox-daemon@tinderbox.mozilla.org';
|
||||
|
||||
# Allow for non-client builds, e.g. camino.
|
||||
#$moz_client_mk = 'client.mk';
|
||||
|
||||
#- Set if you want to build in a separate object tree
|
||||
$ObjDir = 'obj-tb-trunk';
|
||||
|
||||
# Extra build name, if needed.
|
||||
$BuildNameExtra = 'Release';
|
||||
|
||||
# User comment, eg. ip address for dhcp builds.
|
||||
# ex: $UserComment = "ip = 208.12.36.108";
|
||||
#$UserComment = 0;
|
||||
|
||||
#-
|
||||
#- The rest should not need to be changed
|
||||
#-
|
||||
|
||||
#- Minimum wait period from start of build to start of next build in minutes.
|
||||
#$BuildSleep = 10;
|
||||
|
||||
#- Until you get the script working. When it works,
|
||||
#- change to the tree you're actually building
|
||||
# CONFIG: $BuildTree = '%buildTree%';
|
||||
$BuildTree = 'MozillaRelease';
|
||||
|
||||
#$BuildName = '';
|
||||
# CONFIG: $BuildTag = '%productTag%_RELEASE';
|
||||
$BuildTag = 'THUNDERBIRD_3_0a2_RELEASE';
|
||||
#$BuildConfigDir = 'mozilla/config';
|
||||
#$Topsrcdir = 'mozilla';
|
||||
|
||||
$BinaryName = 'thunderbird-bin';
|
||||
|
||||
#
|
||||
# For embedding app, use:
|
||||
#$EmbedBinaryName = 'TestGtkEmbed';
|
||||
#$EmbedDistDir = 'dist/bin'
|
||||
|
||||
|
||||
#$ShellOverride = ''; # Only used if the default shell is too stupid
|
||||
#$ConfigureArgs = '';
|
||||
#$ConfigureEnvArgs = '';
|
||||
#$Compiler = 'gcc';
|
||||
#$NSPRArgs = '';
|
||||
#$ShellOverride = '';
|
||||
|
||||
# allow override of timezone value (for win32 POSIX::strftime)
|
||||
#$Timezone = '';
|
||||
|
||||
# Release build options
|
||||
$ReleaseBuild = 1;
|
||||
$ReleaseToLatest = 0; # Push the release to latest-<milestone>?
|
||||
$ReleaseToDated = 1; # Push the release to YYYY-MM-DD-HH-<milestone>?
|
||||
$shiptalkback = 0;
|
||||
$build_hour = "3";
|
||||
$package_creation_path = "/mail/installer";
|
||||
$ssh_version = "2";
|
||||
# CONFIG: $ssh_user = "%sshUser%";
|
||||
$ssh_user = "cltbld";
|
||||
# CONFIG: $ssh_server = "%sshServer%";
|
||||
$ssh_server = "stage-old.mozilla.org";
|
||||
#$ReleaseGroup = "thunderbird";
|
||||
$ftp_path = "/home/ftp/pub/thunderbird/nightly";
|
||||
$url_path = "http://ftp.mozilla.org/pub/mozilla.org/thunderbird/nightly";
|
||||
$tbox_ftp_path = "/home/ftp/pub/thunderbird/tinderbox-builds";
|
||||
$tbox_url_path = "http://ftp.mozilla.org/pub/mozilla.org/thunderbird/tinderbox-builds";
|
||||
# CONFIG: $milestone = 'thunderbird%version%';
|
||||
$milestone = 'thunderbird3.0a2';
|
||||
$notify_list = "build-announce\@mozilla.org";
|
||||
$stub_installer = 0;
|
||||
$sea_installer = 0;
|
||||
$archive = 1;
|
||||
$update_package = 1;
|
||||
$update_product = "Thunderbird";
|
||||
$update_version = "trunk";
|
||||
$update_platform = "Linux_x86-gcc3";
|
||||
$update_hash = "sha1";
|
||||
$update_filehost = "ftp.mozilla.org";
|
||||
$update_ver_file = "mail/config/version.txt";
|
||||
$update_pushinfo = 0;
|
||||
$crashreporter_buildsymbols = 1;
|
||||
$crashreporter_pushsymbols = 1;
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_HOST'} = '%symbolServer%';
|
||||
$ENV{'SYMBOL_SERVER_HOST'} = 'dm-symbolpush01.mozilla.org';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_USER'} = '%symbolServerUser%';
|
||||
$ENV{'SYMBOL_SERVER_USER'} = 'tbirdbld';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_PATH'} = '%symbolServerPath%';
|
||||
$ENV{'SYMBOL_SERVER_PATH'} = '/mnt/netapp/breakpad/symbols_tbrd';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_SSH_KEY'} = '%symbolServerKey%';
|
||||
$ENV{'SYMBOL_SERVER_SSH_KEY'} = '/home/cltbld/.ssh/tbirdbld_dsa';
|
||||
|
||||
# Reboot the OS at the end of build-and-test cycle. This is primarily
|
||||
# intended for Win9x, which can't last more than a few cycles before
|
||||
# locking up (and testing would be suspect even after a couple of cycles).
|
||||
# Right now, there is only code to force the reboot for Win9x, so even
|
||||
# setting this to 1, will not have an effect on other platforms. Setting
|
||||
# up win9x to automatically logon and begin running tinderbox is left
|
||||
# as an exercise to the reader.
|
||||
#$RebootSystem = 0;
|
||||
|
||||
# LogCompression specifies the type of compression used on the log file.
|
||||
# Valid options are 'gzip', and 'bzip2'. Please make sure the binaries
|
||||
# for 'gzip' or 'bzip2' are in the user's path before setting this
|
||||
# option.
|
||||
#$LogCompression = 'bzip2';
|
||||
|
||||
# LogEncoding specifies the encoding format used for the logs. Valid
|
||||
# options are 'base64', and 'uuencode'. If $LogCompression is set above,
|
||||
# this needs to be set to 'base64' or 'uuencode' to ensure that the
|
||||
# binary data is transferred properly.
|
||||
#$LogEncoding = 'base64';
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Clobbering to force nightly due to nightly bustage from bug 428672.
|
||||
28
mozilla/tools/tinderbox-configs/thunderbird/macosx/mozconfig
Normal file
28
mozilla/tools/tinderbox-configs/thunderbird/macosx/mozconfig
Normal file
@@ -0,0 +1,28 @@
|
||||
#
|
||||
## hostname: bm-xserve07.build.mozilla.org
|
||||
## uname: Darwin bm-xserve07.build.mozilla.org 8.8.4 Darwin Kernel Version 8.8.4: Sun Oct 29 15:26:54 PST 2006; root:xnu-792.16.4.obj~1/RELEASE_I386 i386 i386
|
||||
#
|
||||
|
||||
# symbols for breakpad
|
||||
export CFLAGS="-g -gfull"
|
||||
export CXXFLAGS="-g -gfull"
|
||||
|
||||
. $topsrcdir/build/macosx/universal/mozconfig
|
||||
|
||||
# Make flags
|
||||
mk_add_options MOZ_CO_PROJECT=mail
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j1"
|
||||
mk_add_options MOZ_CO_MODULE="mozilla/tools/update-packaging"
|
||||
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/../build/universal
|
||||
|
||||
# Configure flags
|
||||
ac_add_options --enable-application=mail
|
||||
ac_add_options --enable-update-channel=beta
|
||||
# Add explicit optimize flags in configure.in, not here - see bug 407794
|
||||
ac_add_options --enable-optimize
|
||||
ac_add_options --disable-debug
|
||||
ac_add_options --disable-tests
|
||||
ac_add_options --enable-static
|
||||
ac_add_options --disable-shared
|
||||
|
||||
ac_add_options --enable-update-packaging
|
||||
@@ -0,0 +1,262 @@
|
||||
#
|
||||
## hostname: bm-xserve07.build.mozilla.org
|
||||
## uname: Darwin bm-xserve07.build.mozilla.org 8.8.4 Darwin Kernel Version 8.8.4: Sun Oct 29 15:26:54 PST 2006; root:xnu-792.16.4.obj~1/RELEASE_I386 i386 i386
|
||||
#
|
||||
|
||||
#- tinder-config.pl - Tinderbox configuration file.
|
||||
#- Uncomment the variables you need to set.
|
||||
#- The default values are the same as the commented variables.
|
||||
|
||||
# $ENV{NO_EM_RESTART} = "1";
|
||||
# $ENV{DYLD_NO_FIX_PREBINDING} = "1";
|
||||
# $ENV{LD_PREBIND_ALLOW_OVERLAP} = "1";
|
||||
$ENV{MOZ_CRASHREPORTER_NO_REPORT} = '1';
|
||||
|
||||
$MacUniversalBinary = 1;
|
||||
|
||||
# $ENV{MOZ_PACKAGE_MSI}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: 0
|
||||
# Values: 0 | 1
|
||||
# Purpose: Controls whether a MSI package is made.
|
||||
# Requires: Windows and a local MakeMSI installation.
|
||||
#$ENV{MOZ_PACKAGE_MSI} = 0;
|
||||
|
||||
# $ENV{MOZ_SYMBOLS_TRANSFER_TYPE}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: scp
|
||||
# Values: scp | rsync
|
||||
# Purpose: Use scp or rsync to transfer symbols to the Talkback server.
|
||||
# Requires: The selected type requires the command be available both locally
|
||||
# and on the Talkback server.
|
||||
#$ENV{MOZ_SYMBOLS_TRANSFER_TYPE} = "scp";
|
||||
|
||||
#- PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
|
||||
$BuildAdministrator = 'build@mozilla.org';
|
||||
#$BuildAdministrator = "$ENV{USER}\@$ENV{HOST}";
|
||||
#$BuildAdministrator = ($ENV{USER} || "cltbld") . "\@" . ($ENV{HOST} || "dhcp");
|
||||
|
||||
#- You'll need to change these to suit your machine's needs
|
||||
#$DisplayServer = ':0.0';
|
||||
|
||||
#- Default values of command-line opts
|
||||
#-
|
||||
#$BuildDepend = 1; # Depend or Clobber
|
||||
#$BuildDebug = 0; # Debug or Opt (Darwin)
|
||||
#$ReportStatus = 1; # Send results to server, or not
|
||||
#$ReportFinalStatus = 1; # Finer control over $ReportStatus.
|
||||
$UseTimeStamp = 0; # Use the CVS 'pull-by-timestamp' option, or not
|
||||
#$BuildOnce = 0; # Build once, don't send results to server
|
||||
#$TestOnly = 0; # Only run tests, don't pull/build
|
||||
#$BuildEmbed = 0; # After building seamonkey, go build embed app.
|
||||
#$SkipMozilla = 0; # Use to debug post-mozilla.pl scripts.
|
||||
#$BuildLocales = 0; # Do l10n packaging?
|
||||
|
||||
# Tests
|
||||
$CleanProfile = 1;
|
||||
#$ResetHomeDirForTests = 1;
|
||||
$ProductName = "Thunderbird";
|
||||
#$VendorName = 'Mozilla';
|
||||
|
||||
$RunMozillaTests = 1; # Allow turning off of all tests if needed.
|
||||
$RegxpcomTest = 1;
|
||||
$AliveTest = 1;
|
||||
#$JavaTest = 0;
|
||||
#$ViewerTest = 0;
|
||||
#$BloatTest = 0; # warren memory bloat test
|
||||
#$BloatTest2 = 0; # dbaron memory bloat test, require tracemalloc
|
||||
#$DomToTextConversionTest = 0;
|
||||
#$XpcomGlueTest = 0;
|
||||
$CodesizeTest = 0; # Z, require mozilla/tools/codesighs
|
||||
$EmbedCodesizeTest = 0; # mZ, require mozilla/tools/codesigns
|
||||
#$MailBloatTest = 0;
|
||||
#$EmbedTest = 0; # Assumes you wanted $BuildEmbed=1
|
||||
#$LayoutPerformanceTest = 0; # Tp
|
||||
#$DHTMLPerformanceTest = 0; # Tdhtml
|
||||
#$QATest = 0;
|
||||
#$XULWindowOpenTest = 0; # Txul
|
||||
#$StartupPerformanceTest = 0; # Ts
|
||||
|
||||
$TestsPhoneHome = 0; # Should test report back to server?
|
||||
|
||||
# $results_server
|
||||
#----------------------------------------------------------------------------
|
||||
# Server on which test results will be accessible. This was originally tegu,
|
||||
# then became axolotl. Once we moved services from axolotl, it was time
|
||||
# to give this service its own hostname to make future transitions easier.
|
||||
# - cmp@mozilla.org
|
||||
#$results_server = "build-graphs.mozilla.org";
|
||||
|
||||
#$pageload_server = "spider"; # localhost
|
||||
|
||||
#
|
||||
# Timeouts, values are in seconds.
|
||||
#
|
||||
#$CVSCheckoutTimeout = 3600;
|
||||
#$CreateProfileTimeout = 45;
|
||||
#$RegxpcomTestTimeout = 120;
|
||||
|
||||
#$AliveTestTimeout = 45;
|
||||
#$ViewerTestTimeout = 45;
|
||||
#$EmbedTestTimeout = 45;
|
||||
#$BloatTestTimeout = 120; # seconds
|
||||
#$MailBloatTestTimeout = 120; # seconds
|
||||
#$JavaTestTimeout = 45;
|
||||
#$DomTestTimeout = 45; # seconds
|
||||
#$XpcomGlueTestTimeout = 15;
|
||||
#$CodesizeTestTimeout = 900; # seconds
|
||||
#$CodesizeTestType = "auto"; # {"auto"|"base"}
|
||||
#$LayoutPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$DHTMLPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$QATestTimeout = 1200; # entire test, seconds
|
||||
#$LayoutPerformanceTestPageTimeout = 30000; # each page, ms
|
||||
#$StartupPerformanceTestTimeout = 15; # seconds
|
||||
#$XULWindowOpenTestTimeout = 150; # seconds
|
||||
|
||||
|
||||
#$MozConfigFileName = 'mozconfig';
|
||||
|
||||
#$UseMozillaProfile = 1;
|
||||
#$MozProfileName = 'default';
|
||||
|
||||
#- Set these to what makes sense for your system
|
||||
#$Make = 'gmake'; # Must be GNU make
|
||||
#$MakeOverrides = '';
|
||||
#$mail = '/bin/mail';
|
||||
#$CVS = 'cvs -q';
|
||||
#$CVSCO = 'checkout -P';
|
||||
|
||||
# win32 usually doesn't have /bin/mail
|
||||
#$blat = 'c:/nstools/bin/blat';
|
||||
#$use_blat = 0;
|
||||
|
||||
# Set moz_cvsroot to something like:
|
||||
# :pserver:$ENV{USER}%netscape.com\@cvs.mozilla.org:/cvsroot
|
||||
# :pserver:anonymous\@cvs-mirror.mozilla.org:/cvsroot
|
||||
#
|
||||
# Note that win32 may not need \@, depends on ' or ".
|
||||
# :pserver:$ENV{USER}%netscape.com@cvs.mozilla.org:/cvsroot
|
||||
|
||||
#$moz_cvsroot = $ENV{CVSROOT};
|
||||
# CONFIG: $moz_cvsroot = '%mozillaCvsroot%';
|
||||
$moz_cvsroot = 'cltbld@cvs.mozilla.org:/cvsroot';
|
||||
|
||||
#- Set these proper values for your tinderbox server
|
||||
#$Tinderbox_server = 'tinderbox-daemon@tinderbox.mozilla.org';
|
||||
|
||||
# Allow for non-client builds, e.g. camino.
|
||||
#$moz_client_mk = 'client.mk';
|
||||
|
||||
#- Set if you want to build in a separate object tree
|
||||
$ObjDir = '../build/universal';
|
||||
|
||||
# Extra build name, if needed.
|
||||
$BuildNameExtra = 'Release';
|
||||
|
||||
# User comment, eg. ip address for dhcp builds.
|
||||
# ex: $UserComment = "ip = 208.12.36.108";
|
||||
#$UserComment = 0;
|
||||
|
||||
#-
|
||||
#- The rest should not need to be changed
|
||||
#-
|
||||
|
||||
#- Minimum wait period from start of build to start of next build in minutes.
|
||||
#$BuildSleep = 10;
|
||||
|
||||
#- Until you get the script working. When it works,
|
||||
#- change to the tree you're actually building
|
||||
#$BuildTree = 'MozillaTest';
|
||||
# CONFIG: $BuildTree = '%buildTree%';
|
||||
$BuildTree = 'MozillaRelease';
|
||||
|
||||
#$BuildName = '';
|
||||
# CONFIG: $BuildTag = '%productTag%_RELEASE';
|
||||
$BuildTag = 'THUNDERBIRD_3_0a2_RELEASE';
|
||||
#$BuildConfigDir = 'mozilla/config';
|
||||
#$Topsrcdir = 'mozilla';
|
||||
|
||||
$BinaryName = 'thunderbird-bin';
|
||||
|
||||
#
|
||||
# For embedding app, use:
|
||||
#$EmbedBinaryName = 'TestGtkEmbed';
|
||||
#$EmbedDistDir = 'dist/bin'
|
||||
|
||||
|
||||
#$ShellOverride = ''; # Only used if the default shell is too stupid
|
||||
#$ConfigureArgs = '';
|
||||
#$ConfigureEnvArgs = '';
|
||||
#$Compiler = 'gcc';
|
||||
#$NSPRArgs = '';
|
||||
#$ShellOverride = '';
|
||||
|
||||
# Release build options
|
||||
$ReleaseBuild = 1;
|
||||
$shiptalkback = 0;
|
||||
$ReleaseToLatest = 0; # Push the release to latest-<milestone>?
|
||||
$ReleaseToDated = 1; # Push the release to YYYY-MM-DD-HH-<milestone>?
|
||||
$build_hour = "3";
|
||||
$package_creation_path = "/mail/installer";
|
||||
# needs setting for mac + talkback: $mac_bundle_path = "/browser/app";
|
||||
$mac_bundle_path = "/mail/app";
|
||||
$ssh_version = "2";
|
||||
# CONFIG: $ssh_user = "%sshUser%";
|
||||
$ssh_user = "cltbld";
|
||||
# CONFIG: $ssh_server = "%sshServer%";
|
||||
$ssh_server = "stage-old.mozilla.org";
|
||||
#$ReleaseGroup = "thunderbird";
|
||||
$ftp_path = "/home/ftp/pub/thunderbird/nightly";
|
||||
$url_path = "http://ftp.mozilla.org/pub/mozilla.org/thunderbird/nightly";
|
||||
$tbox_ftp_path = "/home/ftp/pub/thunderbird/tinderbox-builds";
|
||||
$tbox_url_path = "http://ftp.mozilla.org/pub/mozilla.org/thunderbird/tinderbox-builds";
|
||||
# CONFIG: $milestone = 'thunderbird%version%';
|
||||
$milestone = 'thunderbird3.0a2';
|
||||
$notify_list = "build-announce\@mozilla.org";
|
||||
$stub_installer = 0;
|
||||
$sea_installer = 0;
|
||||
$archive = 1;
|
||||
$push_raw_xpis = 0;
|
||||
$update_package = 1;
|
||||
$update_product = "Thunderbird";
|
||||
$update_version = "trunk";
|
||||
$update_platform = "Darwin_Universal-gcc3";
|
||||
$update_hash = "sha1";
|
||||
$update_filehost = "ftp.mozilla.org";
|
||||
$update_ver_file = "mail/config/version.txt";
|
||||
$update_pushinfo = 0;
|
||||
$crashreporter_buildsymbols = 1;
|
||||
$crashreporter_pushsymbols = 1;
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_HOST'} = '%symbolServer%';
|
||||
$ENV{'SYMBOL_SERVER_HOST'} = 'dm-symbolpush01.mozilla.org';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_USER'} = '%symbolServerUser%';
|
||||
$ENV{'SYMBOL_SERVER_USER'} = 'tbirdbld';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_PATH'} = '%symbolServerPath%';
|
||||
$ENV{'SYMBOL_SERVER_PATH'} = '/mnt/netapp/breakpad/symbols_tbrd';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_SSH_KEY'} = '%symbolServerKey%';
|
||||
$ENV{'SYMBOL_SERVER_SSH_KEY'} = '/Users/cltbld/.ssh/tbirdbld_dsa';
|
||||
|
||||
# Reboot the OS at the end of build-and-test cycle. This is primarily
|
||||
# intended for Win9x, which can't last more than a few cycles before
|
||||
# locking up (and testing would be suspect even after a couple of cycles).
|
||||
# Right now, there is only code to force the reboot for Win9x, so even
|
||||
# setting this to 1, will not have an effect on other platforms. Setting
|
||||
# up win9x to automatically logon and begin running tinderbox is left
|
||||
# as an exercise to the reader.
|
||||
#$RebootSystem = 0;
|
||||
|
||||
# LogCompression specifies the type of compression used on the log file.
|
||||
# Valid options are 'gzip', and 'bzip2'. Please make sure the binaries
|
||||
# for 'gzip' or 'bzip2' are in the user's path before setting this
|
||||
# option.
|
||||
#$LogCompression = '';
|
||||
|
||||
# LogEncoding specifies the encoding format used for the logs. Valid
|
||||
# options are 'base64', and 'uuencode'. If $LogCompression is set above,
|
||||
# this needs to be set to 'base64' or 'uuencode' to ensure that the
|
||||
# binary data is transferred properly.
|
||||
#$LogEncoding = '';
|
||||
|
||||
# Prevent Extension Manager from spawning child processes during tests
|
||||
# - processes that tbox scripts cannot kill.
|
||||
#$ENV{NO_EM_RESTART} = '1';
|
||||
@@ -0,0 +1 @@
|
||||
Clobbering to force nightly due to nightly bustage from bug 428672.
|
||||
22
mozilla/tools/tinderbox-configs/thunderbird/win32/mozconfig
Normal file
22
mozilla/tools/tinderbox-configs/thunderbird/win32/mozconfig
Normal file
@@ -0,0 +1,22 @@
|
||||
#
|
||||
## hostname: tbnewref-win32-tbox
|
||||
## MINGW32_NT-5.2 TBNEWREF-WIN32- 1.0.11(0.46/3/2) 2007-01-12 12:05 i686 Msys
|
||||
#
|
||||
|
||||
mk_add_options MOZ_CO_PROJECT=mail
|
||||
mk_add_options MOZ_DEBUG_SYMBOLS=1
|
||||
mk_add_options MOZ_MAKE_FLAGS=-j1
|
||||
mk_add_options MOZ_CO_MODULE="mozilla/tools/update-packaging"
|
||||
|
||||
ac_add_options --enable-application=mail
|
||||
ac_add_options --enable-update-channel=beta
|
||||
ac_add_options --disable-debug
|
||||
# Add explicit optimize flags in configure.in, not here - see bug 407794
|
||||
ac_add_options --enable-optimize
|
||||
ac_add_options --disable-tests
|
||||
ac_add_options --disable-shared
|
||||
ac_add_options --enable-static
|
||||
ac_add_options --enable-update-packaging
|
||||
|
||||
export WIN32_REDIST_DIR="/d/msvs8/VC/redist/x86/Microsoft.VC80.CRT"
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
#
|
||||
## hostname: tbnewref-win32-tbox
|
||||
## MINGW32_NT-5.2 TBNEWREF-WIN32- 1.0.11(0.46/3/2) 2007-01-12 12:05 i686 Msys
|
||||
#
|
||||
|
||||
#- tinder-config.pl - Tinderbox configuration file.
|
||||
#- Uncomment the variables you need to set.
|
||||
#- The default values are the same as the commented variables.
|
||||
|
||||
$ENV{CVSROOT}=":ext:tbirdbld\@cvs.mozilla.org:/cvsroot";
|
||||
$ENV{MOZ_INSTALLER_USE_7ZIP}="1";
|
||||
$ENV{MOZ_PACKAGE_MSI} = 0;
|
||||
$ENV{MOZ_CRASHREPORTER_NO_REPORT} = '1';
|
||||
# Both these two variables are for source server support
|
||||
$ENV{PDBSTR_PATH} = 'C:\\Program Files\\Debugging Tools for Windows\\sdk\\srcsrv\\pdbstr.exe';
|
||||
$ENV{SRCSRV_ROOT} = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot';
|
||||
|
||||
#- PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
|
||||
#$BuildAdministrator = "$ENV{USER}\@$ENV{HOST}";
|
||||
#$BuildAdministrator = ($ENV{USER} || "cltbld") . "\@" . ($ENV{HOST} || "dhcp");
|
||||
|
||||
#- You'll need to change these to suit your machine's needs
|
||||
#$DisplayServer = ':0.0';
|
||||
|
||||
#- Default values of command-line opts
|
||||
#-
|
||||
$BuildDepend = 0; # Depend or Clobber
|
||||
#$BuildDebug = 0; # Debug or Opt (Darwin)
|
||||
#$ReportStatus = 1; # Send results to server, or not
|
||||
#$ReportFinalStatus = 1; # Finer control over $ReportStatus.
|
||||
$UseTimeStamp = 0; # Use the CVS 'pull-by-timestamp' option, or not
|
||||
#$BuildOnce = 0; # Build once, don't send results to server
|
||||
#$TestOnly = 0; # Only run tests, don't pull/build
|
||||
#$BuildEmbed = 0; # After building seamonkey, go build embed app.
|
||||
#$SkipMozilla = 0; # Use to debug post-mozilla.pl scripts.
|
||||
#$BuildLocales = 0; # Do l10n packaging?
|
||||
|
||||
# Tests
|
||||
$CleanProfile = 1;
|
||||
#$ResetHomeDirForTests = 1;
|
||||
$ProductName = "Thunderbird";
|
||||
#$VendorName = '';
|
||||
|
||||
$RunMozillaTests = 1; # Allow turning off of all tests if needed.
|
||||
#$RegxpcomTest = 1;
|
||||
#$AliveTest = 1;
|
||||
#$JavaTest = 0;
|
||||
#$ViewerTest = 0;
|
||||
#$BloatTest = 0; # warren memory bloat test
|
||||
#$BloatTest2 = 0; # dbaron memory bloat test, require tracemalloc
|
||||
#$DomToTextConversionTest = 0;
|
||||
#$XpcomGlueTest = 0;
|
||||
#$CodesizeTest = 0; # Z, require mozilla/tools/codesighs
|
||||
#$EmbedCodesizeTest = 0; # mZ, require mozilla/tools/codesigns
|
||||
#$MailBloatTest = 0;
|
||||
#$EmbedTest = 0; # Assumes you wanted $BuildEmbed=1
|
||||
#$LayoutPerformanceTest = 0; # Tp
|
||||
#$DHTMLPerformanceTest = 0; # Tdhtml
|
||||
#$QATest = 0;
|
||||
#$XULWindowOpenTest = 0; # Txul
|
||||
#$StartupPerformanceTest = 0; # Ts
|
||||
|
||||
$TestsPhoneHome = 0; # Should test report back to server?
|
||||
#$results_server = "axolotl.mozilla.org"; # was tegu
|
||||
#$pageload_server = "spider"; # localhost
|
||||
|
||||
#
|
||||
# Timeouts, values are in seconds.
|
||||
#
|
||||
#$CVSCheckoutTimeout = 3600;
|
||||
#$CreateProfileTimeout = 45;
|
||||
#$RegxpcomTestTimeout = 120;
|
||||
|
||||
#$AliveTestTimeout = 45;
|
||||
#$ViewerTestTimeout = 45;
|
||||
#$EmbedTestTimeout = 45;
|
||||
#$BloatTestTimeout = 120; # seconds
|
||||
#$MailBloatTestTimeout = 120; # seconds
|
||||
#$JavaTestTimeout = 45;
|
||||
#$DomTestTimeout = 45; # seconds
|
||||
#$XpcomGlueTestTimeout = 15;
|
||||
#$CodesizeTestTimeout = 900; # seconds
|
||||
#$CodesizeTestType = "auto"; # {"auto"|"base"}
|
||||
#$LayoutPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$DHTMLPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$QATestTimeout = 1200; # entire test, seconds
|
||||
#$LayoutPerformanceTestPageTimeout = 30000; # each page, ms
|
||||
#$StartupPerformanceTestTimeout = 15; # seconds
|
||||
#$XULWindowOpenTestTimeout = 150; # seconds
|
||||
|
||||
|
||||
#$MozConfigFileName = 'mozconfig';
|
||||
|
||||
#$UseMozillaProfile = 1;
|
||||
#$MozProfileName = 'default';
|
||||
|
||||
#- Set these to what makes sense for your system
|
||||
$Make = 'make'; # Must be GNU make
|
||||
#$MakeOverrides = '';
|
||||
#$mail = '/bin/mail';
|
||||
#$CVS = 'cvs -q';
|
||||
#$CVSCO = 'checkout -P';
|
||||
|
||||
# win32 usually doesn't have /bin/mail
|
||||
$blat = '/d/mozilla-build/blat261/full/blat';
|
||||
$use_blat = 0;
|
||||
|
||||
# Set moz_cvsroot to something like:
|
||||
# :pserver:$ENV{USER}%netscape.com\@cvs.mozilla.org:/cvsroot
|
||||
# :pserver:anonymous\@cvs-mirror.mozilla.org:/cvsroot
|
||||
#
|
||||
# Note that win32 may not need \@, depends on ' or ".
|
||||
# :pserver:$ENV{USER}%netscape.com@cvs.mozilla.org:/cvsroot
|
||||
|
||||
# CONFIG: $moz_cvsroot = '%mozillaCvsroot%';
|
||||
$moz_cvsroot = 'cltbld@cvs.mozilla.org:/cvsroot';
|
||||
|
||||
#- Set these proper values for your tinderbox server
|
||||
#$Tinderbox_server = 'tinderbox-daemon@tinderbox.mozilla.org';
|
||||
|
||||
# Allow for non-client builds, e.g. camino.
|
||||
#$moz_client_mk = 'client.mk';
|
||||
|
||||
#- Set if you want to build in a separate object tree
|
||||
$ObjDir = 'obj-tb-trunk';
|
||||
|
||||
# Extra build name, if needed.
|
||||
$BuildNameExtra = 'Release';
|
||||
|
||||
# User comment, eg. ip address for dhcp builds.
|
||||
# ex: $UserComment = "ip = 208.12.36.108";
|
||||
#$UserComment = 0;
|
||||
|
||||
#-
|
||||
#- The rest should not need to be changed
|
||||
#-
|
||||
|
||||
#- Minimum wait period from start of build to start of next build in minutes.
|
||||
#$BuildSleep = 10;
|
||||
|
||||
#- Until you get the script working. When it works,
|
||||
#- change to the tree you're actually building
|
||||
# CONFIG: $BuildTree = '%buildTree%';
|
||||
$BuildTree = 'MozillaRelease';
|
||||
|
||||
#$BuildName = '';
|
||||
# CONFIG: $BuildTag = '%productTag%_RELEASE';
|
||||
$BuildTag = 'THUNDERBIRD_3_0a2_RELEASE';
|
||||
#$BuildConfigDir = 'mozilla/config';
|
||||
#$Topsrcdir = 'mozilla';
|
||||
|
||||
$BinaryName = 'thunderbird.exe';
|
||||
|
||||
#
|
||||
# For embedding app, use:
|
||||
#$EmbedBinaryName = 'TestGtkEmbed';
|
||||
#$EmbedDistDir = 'dist/bin'
|
||||
|
||||
|
||||
#$ShellOverride = ''; # Only used if the default shell is too stupid
|
||||
#$ConfigureArgs = '';
|
||||
#$ConfigureEnvArgs = '';
|
||||
#$Compiler = 'gcc';
|
||||
#$NSPRArgs = '';
|
||||
#$ShellOverride = '';
|
||||
|
||||
# Release build options
|
||||
$ReleaseBuild = 1;
|
||||
$shiptalkback = 0;
|
||||
$ReleaseToLatest = 0;
|
||||
$ReleaseToDated = 1;
|
||||
$build_hour = "3";
|
||||
$package_creation_path = "/mail/installer";
|
||||
# needs setting for mac + talkback: $mac_bundle_path = "/browser/app";
|
||||
$ssh_version = "2";
|
||||
# CONFIG: $ssh_user = "%sshUser%";
|
||||
$ssh_user = "cltbld";
|
||||
# CONFIG: $ssh_server = "%sshServer%";
|
||||
$ssh_server = "stage-old.mozilla.org";
|
||||
#$ReleaseGroup = "thunderbird";
|
||||
$ftp_path = "/home/ftp/pub/thunderbird/nightly";
|
||||
$url_path = "http://ftp.mozilla.org/pub/mozilla.org/thunderbird/nightly";
|
||||
$tbox_ftp_path = "/home/ftp/pub/thunderbird/tinderbox-builds";
|
||||
$tbox_url_path = "http://ftp.mozilla.org/pub/mozilla.org/thunderbird/tinderbox-builds";
|
||||
# CONFIG: $milestone = 'thunderbird%version%';
|
||||
$milestone = 'thunderbird3.0a2';
|
||||
$notify_list = "build-announce\@mozilla.org";
|
||||
$stub_installer = 0;
|
||||
$sea_installer = 1;
|
||||
$archive = 1;
|
||||
$push_raw_xpis = 1;
|
||||
|
||||
$update_package = 1;
|
||||
$update_product = "Thunderbird";
|
||||
$update_version = "trunk";
|
||||
$update_ver_file = "mail/config/version.txt";
|
||||
$update_platform = "WINNT_x86-msvc";
|
||||
$update_hash = "sha1";
|
||||
$update_filehost = "ftp.mozilla.org";
|
||||
$update_pushinfo = 0;
|
||||
$crashreporter_buildsymbols = 1;
|
||||
$crashreporter_pushsymbols = 1;
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_HOST'} = '%symbolServer%';
|
||||
$ENV{'SYMBOL_SERVER_HOST'} = 'dm-symbolpush01.mozilla.org';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_USER'} = '%symbolServerUser%';
|
||||
$ENV{'SYMBOL_SERVER_USER'} = 'tbirdbld';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_PATH'} = '%symbolServerPath%';
|
||||
$ENV{'SYMBOL_SERVER_PATH'} = '/mnt/netapp/breakpad/symbols_tbrd';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_SSH_KEY'} = '%symbolServerKey%';
|
||||
$ENV{'SYMBOL_SERVER_SSH_KEY'} = '/c/Documents and Settings/cltbld/.ssh/tbirdbld_dsa';
|
||||
|
||||
# Reboot the OS at the end of build-and-test cycle. This is primarily
|
||||
# intended for Win9x, which can't last more than a few cycles before
|
||||
# locking up (and testing would be suspect even after a couple of cycles).
|
||||
# Right now, there is only code to force the reboot for Win9x, so even
|
||||
# setting this to 1, will not have an effect on other platforms. Setting
|
||||
# up win9x to automatically logon and begin running tinderbox is left
|
||||
# as an exercise to the reader.
|
||||
#$RebootSystem = 0;
|
||||
|
||||
# LogCompression specifies the type of compression used on the log file.
|
||||
# Valid options are 'gzip', and 'bzip2'. Please make sure the binaries
|
||||
# for 'gzip' or 'bzip2' are in the user's path before setting this
|
||||
# option.
|
||||
#$LogCompression = '';
|
||||
|
||||
# LogEncoding specifies the encoding format used for the logs. Valid
|
||||
# options are 'base64', and 'uuencode'. If $LogCompression is set above,
|
||||
# this needs to be set to 'base64' or 'uuencode' to ensure that the
|
||||
# binary data is transferred properly.
|
||||
#$LogEncoding = '';
|
||||
|
||||
# Prevent Extension Manager from spawning child processes during tests
|
||||
# - processes that tbox scripts cannot kill.
|
||||
#$ENV{NO_EM_RESTART} = '1';
|
||||
1
mozilla/tools/tinderbox-configs/xulrunner/linux/CLOBBER
Normal file
1
mozilla/tools/tinderbox-configs/xulrunner/linux/CLOBBER
Normal file
@@ -0,0 +1 @@
|
||||
Clobbering to fix up checkout issues
|
||||
17
mozilla/tools/tinderbox-configs/xulrunner/linux/mozconfig
Normal file
17
mozilla/tools/tinderbox-configs/xulrunner/linux/mozconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
## hostname: xr-linux-tbox
|
||||
## uname: Linux xr-linux-tbox.build.mozilla.org 2.6.18-8.el5 #1 SMP Thu Mar 15 19:57:35 EDT 2007 i686 i686 i386 GNU/Linux
|
||||
#
|
||||
|
||||
export MOZILLA_OFFICIAL=1
|
||||
export JAVA_HOME=/tools/jdk
|
||||
mk_add_options MOZILLA_OFFICIAL=1
|
||||
|
||||
mk_add_options MOZ_CO_PROJECT=xulrunner
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j3"
|
||||
|
||||
ac_add_options --enable-application=xulrunner
|
||||
ac_add_options --disable-tests
|
||||
|
||||
CC=/tools/gcc-4.1.1/bin/gcc
|
||||
CXX=/tools/gcc-4.1.1/bin/g++
|
||||
262
mozilla/tools/tinderbox-configs/xulrunner/linux/tinder-config.pl
Normal file
262
mozilla/tools/tinderbox-configs/xulrunner/linux/tinder-config.pl
Normal file
@@ -0,0 +1,262 @@
|
||||
#
|
||||
## hostname: xr-linux-tbox
|
||||
## uname: Linux xr-linux-tbox.build.mozilla.org 2.6.18-8.el5 #1 SMP Thu Mar 15 19:57:35 EDT 2007 i686 i686 i386 GNU/Linux
|
||||
#
|
||||
|
||||
#- tinder-config.pl - Tinderbox configuration file.
|
||||
#- Uncomment the variables you need to set.
|
||||
#- The default values are the same as the commented variables.
|
||||
|
||||
$ENV{MOZ_CRASHREPORTER_NO_REPORT} = '1';
|
||||
|
||||
# $ENV{MOZ_PACKAGE_MSI}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: 0
|
||||
# Values: 0 | 1
|
||||
# Purpose: Controls whether a MSI package is made.
|
||||
# Requires: Windows and a local MakeMSI installation.
|
||||
#$ENV{MOZ_PACKAGE_MSI} = 0;
|
||||
|
||||
# $ENV{MOZ_SYMBOLS_TRANSFER_TYPE}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: scp
|
||||
# Values: scp | rsync
|
||||
# Purpose: Use scp or rsync to transfer symbols to the Talkback server.
|
||||
# Requires: The selected type requires the command be available both locally
|
||||
# and on the Talkback server.
|
||||
#$ENV{MOZ_SYMBOLS_TRANSFER_TYPE} = "scp";
|
||||
|
||||
#- PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
|
||||
#$BuildAdministrator = "$ENV{USER}\@$ENV{HOST}";
|
||||
#$BuildAdministrator = ($ENV{USER} || "cltbld") . "\@" . ($ENV{HOST} || "dhcp");
|
||||
$BuildAdministrator = "build\@mozilla.org";
|
||||
|
||||
#- You'll need to change these to suit your machine's needs
|
||||
#$DisplayServer = ':0.0';
|
||||
|
||||
#- Default values of command-line opts
|
||||
#-
|
||||
#$BuildDepend = 1; # Depend or Clobber
|
||||
#$BuildDebug = 0; # Debug or Opt (Darwin)
|
||||
#$ReportStatus = 1; # Send results to server, or not
|
||||
#$ReportFinalStatus = 1; # Finer control over $ReportStatus.
|
||||
$UseTimeStamp = 0; # Use the CVS 'pull-by-timestamp' option, or not
|
||||
#$BuildOnce = 0; # Build once, don't send results to server
|
||||
#$ConfigureOnly = 0; # Configure, but do not build.
|
||||
#$TestOnly = 0; # Only run tests, don't pull/build
|
||||
#$BuildEmbed = 0; # After building seamonkey, go build embed app.
|
||||
#$SkipMozilla = 0; # Use to debug post-mozilla.pl scripts.
|
||||
#$BuildLocales = 0; # Do l10n packaging?
|
||||
$BuildSDK = 1; # Build the SDK
|
||||
|
||||
# Only used when $BuildLocales = 1
|
||||
%WGetFiles = (); # Pull files from the web, URL => Location
|
||||
#$WGetTimeout = 360; # Wget timeout, in seconds
|
||||
#$BuildLocalesArgs = ""; # Extra attributes to add to the makefile command
|
||||
# which builds the "installers-<locale>" target.
|
||||
# Typically used to set ZIP_IN and WIN32_INSTALLER_IN
|
||||
|
||||
# Tests
|
||||
$CleanProfile = 1;
|
||||
#$ResetHomeDirForTests = 1;
|
||||
$ProductName = "XULRunner";
|
||||
$VendorName = 'Mozilla';
|
||||
|
||||
$RunMozillaTests = 0; # Allow turning off of all tests if needed.
|
||||
#$RegxpcomTest = 1;
|
||||
#$AliveTest = 1;
|
||||
#$JavaTest = 0;
|
||||
#$ViewerTest = 0;
|
||||
#$BloatTest = 0; # warren memory bloat test
|
||||
#$BloatTest2 = 0; # dbaron memory bloat test, require tracemalloc
|
||||
#$DomToTextConversionTest = 0;
|
||||
#$XpcomGlueTest = 0;
|
||||
#$CodesizeTest = 0; # Z, require mozilla/tools/codesighs
|
||||
#$EmbedCodesizeTest = 0; # mZ, require mozilla/tools/codesigns
|
||||
#$MailBloatTest = 0;
|
||||
#$EmbedTest = 0; # Assumes you wanted $BuildEmbed=1
|
||||
#$LayoutPerformanceTest = 0; # Tp
|
||||
#$DHTMLPerformanceTest = 0; # Tdhtml
|
||||
#$QATest = 0;
|
||||
#$XULWindowOpenTest = 0; # Txul
|
||||
#$StartupPerformanceTest = 0; # Ts
|
||||
#@CompareLocaleDirs = (); # Run compare-locales test on these directories
|
||||
# ("network","dom","toolkit","security/manager");
|
||||
#$CompareLocalesAviary = 0; # Should the compare-locales commands use the
|
||||
# aviary directory structure?
|
||||
|
||||
#$TestsPhoneHome = 0; # Should test report back to server?
|
||||
|
||||
# $results_server
|
||||
#----------------------------------------------------------------------------
|
||||
# Server on which test results will be accessible. This was originally tegu,
|
||||
# then became axolotl. Once we moved services from axolotl, it was time
|
||||
# to give this service its own hostname to make future transitions easier.
|
||||
# - cmp@mozilla.org
|
||||
#$results_server = "build-graphs.mozilla.org";
|
||||
|
||||
#$pageload_server = "spider"; # localhost
|
||||
|
||||
#
|
||||
# Timeouts, values are in seconds.
|
||||
#
|
||||
#$CVSCheckoutTimeout = 3600;
|
||||
#$CreateProfileTimeout = 45;
|
||||
#$RegxpcomTestTimeout = 120;
|
||||
|
||||
#$AliveTestTimeout = 45;
|
||||
#$ViewerTestTimeout = 45;
|
||||
#$EmbedTestTimeout = 45;
|
||||
#$BloatTestTimeout = 120; # seconds
|
||||
#$MailBloatTestTimeout = 120; # seconds
|
||||
#$JavaTestTimeout = 45;
|
||||
#$DomTestTimeout = 45; # seconds
|
||||
#$XpcomGlueTestTimeout = 15;
|
||||
#$CodesizeTestTimeout = 900; # seconds
|
||||
#$CodesizeTestType = "auto"; # {"auto"|"base"}
|
||||
#$LayoutPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$DHTMLPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$QATestTimeout = 1200; # entire test, seconds
|
||||
#$LayoutPerformanceTestPageTimeout = 30000; # each page, ms
|
||||
#$StartupPerformanceTestTimeout = 15; # seconds
|
||||
#$XULWindowOpenTestTimeout = 150; # seconds
|
||||
|
||||
|
||||
#$MozConfigFileName = 'mozconfig';
|
||||
|
||||
#$UseMozillaProfile = 1;
|
||||
#$MozProfileName = 'default';
|
||||
|
||||
#- Set these to what makes sense for your system
|
||||
#$Make = 'gmake'; # Must be GNU make
|
||||
#$MakeOverrides = '';
|
||||
#$mail = '/bin/mail';
|
||||
#$CVS = 'cvs -q';
|
||||
#$CVSCO = 'checkout -P';
|
||||
|
||||
# win32 usually doesn't have /bin/mail
|
||||
#$blat = 'c:/nstools/bin/blat';
|
||||
#$use_blat = 0;
|
||||
|
||||
# Set moz_cvsroot to something like:
|
||||
# :pserver:$ENV{USER}%netscape.com\@cvs.mozilla.org:/cvsroot
|
||||
# :pserver:anonymous\@cvs-mirror.mozilla.org:/cvsroot
|
||||
#
|
||||
# Note that win32 may not need \@, depends on ' or ".
|
||||
# :pserver:$ENV{USER}%netscape.com@cvs.mozilla.org:/cvsroot
|
||||
|
||||
#$moz_cvsroot = $ENV{CVSROOT};
|
||||
# CONFIG: $moz_cvsroot = '%mozillaCvsroot%';
|
||||
$moz_cvsroot = 'cltbld@cvs.mozilla.org:/cvsroot';
|
||||
|
||||
#- Set these proper values for your tinderbox server
|
||||
#$Tinderbox_server = 'tinderbox-daemon@tinderbox.mozilla.org';
|
||||
|
||||
# Allow for non-client builds, e.g. camino.
|
||||
#$moz_client_mk = 'client.mk';
|
||||
|
||||
#- Set if you want to build in a separate object tree
|
||||
$ObjDir = 'obj-xulrunner';
|
||||
|
||||
# Extra build name, if needed.
|
||||
$BuildNameExtra = 'Release';
|
||||
|
||||
# User comment, eg. ip address for dhcp builds.
|
||||
# ex: $UserComment = "ip = 208.12.36.108";
|
||||
#$UserComment = 0;
|
||||
|
||||
#-
|
||||
#- The rest should not need to be changed
|
||||
#-
|
||||
|
||||
#- Minimum wait period from start of build to start of next build in minutes.
|
||||
#$BuildSleep = 10;
|
||||
|
||||
#- Until you get the script working. When it works,
|
||||
#- change to the tree you're actually building
|
||||
# CONFIG: $BuildTree = '%buildTree%';
|
||||
$BuildTree = 'MozillaRelease';
|
||||
|
||||
#$BuildName = '';
|
||||
# CONFIG: $BuildTag = '%productTag%_RELEASE';
|
||||
$BuildTag = 'FIREFOX_3_0_17_RELEASE';
|
||||
#$BuildConfigDir = 'mozilla/config';
|
||||
#$Topsrcdir = 'mozilla';
|
||||
|
||||
$BinaryName = 'xulrunner-bin';
|
||||
|
||||
#
|
||||
# For embedding app, use:
|
||||
#$EmbedBinaryName = 'TestGtkEmbed';
|
||||
#$EmbedDistDir = 'dist/bin'
|
||||
|
||||
|
||||
#$ShellOverride = ''; # Only used if the default shell is too stupid
|
||||
#$ConfigureArgs = '';
|
||||
#$ConfigureEnvArgs = '';
|
||||
#$Compiler = 'gcc';
|
||||
#$NSPRArgs = '';
|
||||
#$ShellOverride = '';
|
||||
|
||||
# Release build options
|
||||
$ReleaseBuild = 1;
|
||||
#$LocaleProduct = "browser";
|
||||
$shiptalkback = 0;
|
||||
$ReleaseToLatest = 0; # Push the release to latest-<milestone>?
|
||||
$ReleaseToDated = 1; # Push the release to YYYY-MM-DD-HH-<milestone>?
|
||||
#$build_hour = "8";
|
||||
$package_creation_path = "/xulrunner/installer";
|
||||
# needs setting for mac + talkback: $mac_bundle_path = "/browser/app";
|
||||
$ssh_version = "2";
|
||||
# CONFIG: $ssh_user = "%sshUser%";
|
||||
$ssh_user = "cltbld";
|
||||
#$ssh_key = "$ENV{HOME}/.ssh/xrbld_dsa";
|
||||
# CONFIG: $ssh_server = "%sshServer%";
|
||||
$ssh_server = "stage-old.mozilla.org";
|
||||
$ReleaseGroup = "xulrunner";
|
||||
$ftp_path = "/home/ftp/pub/xulrunner/nightly";
|
||||
$url_path = "http://ftp.mozilla.org/pub/mozilla.org/xulrunner/nightly";
|
||||
$tbox_ftp_path = "/home/ftp/pub/xulrunner/tinderbox-builds";
|
||||
$tbox_url_path = "http://ftp.mozilla.org/pub/mozilla.org/xulrunner/tinderbox-builds";
|
||||
# CONFIG: $milestone = "xulrunner%version%";
|
||||
$milestone = "xulrunner1.9.0.17";
|
||||
$notify_list = "build-announce\@mozilla.org";
|
||||
$stub_installer = 0;
|
||||
$sea_installer = 0;
|
||||
$archive = 1;
|
||||
$push_raw_xpis = 0;
|
||||
$crashreporter_buildsymbols = 0;
|
||||
$crashreporter_pushsymbols = 0;
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_HOST'} = '%symbolServer%';
|
||||
$ENV{'SYMBOL_SERVER_HOST'} = 'stage-old.mozilla.org';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_USER'} = '%symbolServerUser%';
|
||||
$ENV{'SYMBOL_SERVER_USER'} = 'xrbld';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_PATH'} = '%symbolServerPath%';
|
||||
$ENV{'SYMBOL_SERVER_PATH'} = '/mnt/netapp/breakpad/symbols_xr';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_SSH_KEY'} = '%symbolServerKey%';
|
||||
$ENV{'SYMBOL_SERVER_SSH_KEY'} = '/home/cltbld/.ssh/xrbld_dsa';
|
||||
|
||||
# Reboot the OS at the end of build-and-test cycle. This is primarily
|
||||
# intended for Win9x, which can't last more than a few cycles before
|
||||
# locking up (and testing would be suspect even after a couple of cycles).
|
||||
# Right now, there is only code to force the reboot for Win9x, so even
|
||||
# setting this to 1, will not have an effect on other platforms. Setting
|
||||
# up win9x to automatically logon and begin running tinderbox is left
|
||||
# as an exercise to the reader.
|
||||
#$RebootSystem = 0;
|
||||
|
||||
# LogCompression specifies the type of compression used on the log file.
|
||||
# Valid options are 'gzip', and 'bzip2'. Please make sure the binaries
|
||||
# for 'gzip' or 'bzip2' are in the user's path before setting this
|
||||
# option.
|
||||
#$LogCompression = '';
|
||||
|
||||
# LogEncoding specifies the encoding format used for the logs. Valid
|
||||
# options are 'base64', and 'uuencode'. If $LogCompression is set above,
|
||||
# this needs to be set to 'base64' or 'uuencode' to ensure that the
|
||||
# binary data is transferred properly.
|
||||
#$LogEncoding = '';
|
||||
|
||||
# Prevent Extension Manager from spawning child processes during tests
|
||||
# - processes that tbox scripts cannot kill.
|
||||
#$ENV{NO_EM_RESTART} = '1';
|
||||
1
mozilla/tools/tinderbox-configs/xulrunner/macosx/CLOBBER
Normal file
1
mozilla/tools/tinderbox-configs/xulrunner/macosx/CLOBBER
Normal file
@@ -0,0 +1 @@
|
||||
CLOBBERing to disable zipwriter from bug 379633
|
||||
20
mozilla/tools/tinderbox-configs/xulrunner/macosx/mozconfig
Normal file
20
mozilla/tools/tinderbox-configs/xulrunner/macosx/mozconfig
Normal file
@@ -0,0 +1,20 @@
|
||||
#
|
||||
## hostname: bm-xserve09.build.mozilla.org
|
||||
## uname: Darwin bm-xserve09.build.mozilla.org 8.8.4 Darwin Kernel Version 8.8.4: Sun Oct 29 15:26:54 PST 2006; root:xnu-792.16.4.obj~1/RELEASE_I386 i386 i386
|
||||
#
|
||||
|
||||
. $topsrcdir/build/macosx/universal/mozconfig
|
||||
|
||||
export MOZILLA_OFFICIAL=1
|
||||
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
|
||||
mk_add_options MOZILLA_OFFICIAL=1
|
||||
|
||||
mk_add_options MOZ_CO_PROJECT=xulrunner
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j8"
|
||||
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/../build/universal
|
||||
|
||||
ac_add_options --enable-application=xulrunner
|
||||
ac_add_options --disable-tests
|
||||
ac_add_options --enable-svg
|
||||
ac_add_options --enable-canvas
|
||||
ac_add_app_options ppc --enable-prebinding
|
||||
@@ -0,0 +1,268 @@
|
||||
#
|
||||
## hostname: bm-xserve09.build.mozilla.org
|
||||
## uname: Darwin bm-xserve09.build.mozilla.org 8.8.4 Darwin Kernel Version 8.8.4: Sun Oct 29 15:26:54 PST 2006; root:xnu-792.16.4.obj~1/RELEASE_I386 i386 i386
|
||||
#
|
||||
|
||||
#- tinder-config.pl - Tinderbox configuration file.
|
||||
#- Uncomment the variables you need to set.
|
||||
#- The default values are the same as the commented variables.
|
||||
|
||||
$MacUniversalBinary = 1;
|
||||
|
||||
$ENV{CHOWN_ROOT} = "/builds/tinderbox/bin/chown_root";
|
||||
$ENV{REVERT_ROOT} = "/builds/tinderbox/bin/revert_root";
|
||||
$ENV{CHOWN_REVERT} = $ENV{REVERT_ROOT};
|
||||
$ENV{MOZ_CRASHREPORTER_NO_REPORT} = '1';
|
||||
|
||||
# $ENV{MOZ_PACKAGE_MSI}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: 0
|
||||
# Values: 0 | 1
|
||||
# Purpose: Controls whether a MSI package is made.
|
||||
# Requires: Windows and a local MakeMSI installation.
|
||||
#$ENV{MOZ_PACKAGE_MSI} = 0;
|
||||
|
||||
# $ENV{MOZ_SYMBOLS_TRANSFER_TYPE}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: scp
|
||||
# Values: scp | rsync
|
||||
# Purpose: Use scp or rsync to transfer symbols to the Talkback server.
|
||||
# Requires: The selected type requires the command be available both locally
|
||||
# and on the Talkback server.
|
||||
#$ENV{MOZ_SYMBOLS_TRANSFER_TYPE} = "scp";
|
||||
|
||||
#- PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
|
||||
#$BuildAdministrator = "$ENV{USER}\@$ENV{HOST}";
|
||||
#$BuildAdministrator = ($ENV{USER} || "cltbld") . "\@" . ($ENV{HOST} || "dhcp");
|
||||
$BuildAdministrator = "build\@mozilla.org";
|
||||
|
||||
#- You'll need to change these to suit your machine's needs
|
||||
#$DisplayServer = ':0.0';
|
||||
|
||||
#- Default values of command-line opts
|
||||
#-
|
||||
#$BuildDepend = 1; # Depend or Clobber
|
||||
#$BuildDebug = 0; # Debug or Opt (Darwin)
|
||||
#$ReportStatus = 1; # Send results to server, or not
|
||||
#$ReportFinalStatus = 1; # Finer control over $ReportStatus.
|
||||
$UseTimeStamp = 0; # Use the CVS 'pull-by-timestamp' option, or not
|
||||
#$BuildOnce = 0; # Build once, don't send results to server
|
||||
#$ConfigureOnly = 0; # Configure, but do not build.
|
||||
#$TestOnly = 0; # Only run tests, don't pull/build
|
||||
#$BuildEmbed = 0; # After building seamonkey, go build embed app.
|
||||
#$SkipMozilla = 0; # Use to debug post-mozilla.pl scripts.
|
||||
#$BuildLocales = 0; # Do l10n packaging?
|
||||
$BuildSDK = 1; # Build the SDK
|
||||
|
||||
# Only used when $BuildLocales = 1
|
||||
%WGetFiles = (); # Pull files from the web, URL => Location
|
||||
#$WGetTimeout = 360; # Wget timeout, in seconds
|
||||
#$BuildLocalesArgs = ""; # Extra attributes to add to the makefile command
|
||||
# which builds the "installers-<locale>" target.
|
||||
# Typically used to set ZIP_IN and WIN32_INSTALLER_IN
|
||||
|
||||
# Tests
|
||||
$CleanProfile = 1;
|
||||
#$ResetHomeDirForTests = 1;
|
||||
$ProductName = "XULRunner";
|
||||
$VendorName = 'Mozilla';
|
||||
|
||||
$RunMozillaTests = 0; # Allow turning off of all tests if needed.
|
||||
#$RegxpcomTest = 1;
|
||||
#$AliveTest = 1;
|
||||
#$JavaTest = 0;
|
||||
#$ViewerTest = 0;
|
||||
#$BloatTest = 0; # warren memory bloat test
|
||||
#$BloatTest2 = 0; # dbaron memory bloat test, require tracemalloc
|
||||
#$DomToTextConversionTest = 0;
|
||||
#$XpcomGlueTest = 0;
|
||||
#$CodesizeTest = 0; # Z, require mozilla/tools/codesighs
|
||||
#$EmbedCodesizeTest = 0; # mZ, require mozilla/tools/codesigns
|
||||
#$MailBloatTest = 0;
|
||||
#$EmbedTest = 0; # Assumes you wanted $BuildEmbed=1
|
||||
#$LayoutPerformanceTest = 0; # Tp
|
||||
#$DHTMLPerformanceTest = 0; # Tdhtml
|
||||
#$QATest = 0;
|
||||
#$XULWindowOpenTest = 0; # Txul
|
||||
#$StartupPerformanceTest = 0; # Ts
|
||||
#@CompareLocaleDirs = (); # Run compare-locales test on these directories
|
||||
# ("network","dom","toolkit","security/manager");
|
||||
#$CompareLocalesAviary = 0; # Should the compare-locales commands use the
|
||||
# aviary directory structure?
|
||||
|
||||
#$TestsPhoneHome = 0; # Should test report back to server?
|
||||
|
||||
# $results_server
|
||||
#----------------------------------------------------------------------------
|
||||
# Server on which test results will be accessible. This was originally tegu,
|
||||
# then became axolotl. Once we moved services from axolotl, it was time
|
||||
# to give this service its own hostname to make future transitions easier.
|
||||
# - cmp@mozilla.org
|
||||
#$results_server = "build-graphs.mozilla.org";
|
||||
|
||||
#$pageload_server = "spider"; # localhost
|
||||
|
||||
#
|
||||
# Timeouts, values are in seconds.
|
||||
#
|
||||
#$CVSCheckoutTimeout = 3600;
|
||||
#$CreateProfileTimeout = 45;
|
||||
#$RegxpcomTestTimeout = 120;
|
||||
|
||||
#$AliveTestTimeout = 45;
|
||||
#$ViewerTestTimeout = 45;
|
||||
#$EmbedTestTimeout = 45;
|
||||
#$BloatTestTimeout = 120; # seconds
|
||||
#$MailBloatTestTimeout = 120; # seconds
|
||||
#$JavaTestTimeout = 45;
|
||||
#$DomTestTimeout = 45; # seconds
|
||||
#$XpcomGlueTestTimeout = 15;
|
||||
#$CodesizeTestTimeout = 900; # seconds
|
||||
#$CodesizeTestType = "auto"; # {"auto"|"base"}
|
||||
#$LayoutPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$DHTMLPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$QATestTimeout = 1200; # entire test, seconds
|
||||
#$LayoutPerformanceTestPageTimeout = 30000; # each page, ms
|
||||
#$StartupPerformanceTestTimeout = 15; # seconds
|
||||
#$XULWindowOpenTestTimeout = 150; # seconds
|
||||
|
||||
|
||||
#$MozConfigFileName = 'mozconfig';
|
||||
|
||||
#$UseMozillaProfile = 1;
|
||||
#$MozProfileName = 'default';
|
||||
|
||||
#- Set these to what makes sense for your system
|
||||
#$Make = 'gmake'; # Must be GNU make
|
||||
#$MakeOverrides = '';
|
||||
#$mail = '/bin/mail';
|
||||
#$CVS = 'cvs -q';
|
||||
#$CVSCO = 'checkout -P';
|
||||
|
||||
# win32 usually doesn't have /bin/mail
|
||||
#$blat = 'c:/nstools/bin/blat';
|
||||
#$use_blat = 0;
|
||||
|
||||
# Set moz_cvsroot to something like:
|
||||
# :pserver:$ENV{USER}%netscape.com\@cvs.mozilla.org:/cvsroot
|
||||
# :pserver:anonymous\@cvs-mirror.mozilla.org:/cvsroot
|
||||
#
|
||||
# Note that win32 may not need \@, depends on ' or ".
|
||||
# :pserver:$ENV{USER}%netscape.com@cvs.mozilla.org:/cvsroot
|
||||
|
||||
# sharing bm-xserve09 with T'bird build, do all CVS pulls with that key
|
||||
# CONFIG: $moz_cvsroot = '%mozillaCvsroot%';
|
||||
$moz_cvsroot = 'cltbld@cvs.mozilla.org:/cvsroot';
|
||||
|
||||
#- Set these proper values for your tinderbox server
|
||||
#$Tinderbox_server = 'tinderbox-daemon@tinderbox.mozilla.org';
|
||||
|
||||
# Allow for non-client builds, e.g. camino.
|
||||
#$moz_client_mk = 'client.mk';
|
||||
|
||||
#- Set if you want to build in a separate object tree
|
||||
$ObjDir = '../build/universal';
|
||||
|
||||
# Extra build name, if needed.
|
||||
$BuildNameExtra = 'Release';
|
||||
|
||||
# User comment, eg. ip address for dhcp builds.
|
||||
# ex: $UserComment = "ip = 208.12.36.108";
|
||||
#$UserComment = 0;
|
||||
|
||||
#-
|
||||
#- The rest should not need to be changed
|
||||
#-
|
||||
|
||||
#- Minimum wait period from start of build to start of next build in minutes.
|
||||
#$BuildSleep = 10;
|
||||
|
||||
#- Until you get the script working. When it works,
|
||||
#- change to the tree you're actually building
|
||||
# CONFIG: $BuildTree = '%buildTree%';
|
||||
$BuildTree = 'MozillaRelease';
|
||||
|
||||
#$BuildName = '';
|
||||
# CONFIG: $BuildTag = '%productTag%_RELEASE';
|
||||
$BuildTag = 'FIREFOX_3_0_17_RELEASE';
|
||||
#$BuildConfigDir = 'mozilla/config';
|
||||
#$Topsrcdir = 'mozilla';
|
||||
|
||||
$BinaryName = 'xulrunner-bin';
|
||||
|
||||
#
|
||||
# For embedding app, use:
|
||||
#$EmbedBinaryName = 'TestGtkEmbed';
|
||||
#$EmbedDistDir = 'dist/bin'
|
||||
|
||||
|
||||
#$ShellOverride = ''; # Only used if the default shell is too stupid
|
||||
#$ConfigureArgs = '';
|
||||
#$ConfigureEnvArgs = '';
|
||||
#$Compiler = 'gcc';
|
||||
#$NSPRArgs = '';
|
||||
#$ShellOverride = '';
|
||||
|
||||
# Release build options
|
||||
$ReleaseBuild = 1;
|
||||
#$LocaleProduct = "browser";
|
||||
$shiptalkback = 0;
|
||||
$ReleaseToLatest = 0; # Push the release to latest-<milestone>?
|
||||
$ReleaseToDated = 1; # Push the release to YYYY-MM-DD-HH-<milestone>?
|
||||
#$build_hour = "8";
|
||||
$package_creation_path = "/xulrunner/installer";
|
||||
# needs setting for mac + talkback: $mac_bundle_path = "/browser/app";
|
||||
$mac_bundle_path = "/browser/app";
|
||||
$ssh_version = "2";
|
||||
# CONFIG: $ssh_user = "%sshUser%";
|
||||
$ssh_user = "cltbld";
|
||||
#$ssh_key = "$ENV{HOME}/.ssh/xrbld_dsa";
|
||||
# CONFIG: $ssh_server = "%sshServer%";
|
||||
$ssh_server = "stage-old.mozilla.org";
|
||||
$ReleaseGroup = "xulrunner";
|
||||
$ftp_path = "/home/ftp/pub/xulrunner/nightly";
|
||||
$url_path = "http://ftp.mozilla.org/pub/mozilla.org/xulrunner/nightly";
|
||||
$tbox_ftp_path = "/home/ftp/pub/xulrunner/tinderbox-builds";
|
||||
$tbox_url_path = "http://ftp.mozilla.org/pub/mozilla.org/xulrunner/tinderbox-builds";
|
||||
# CONFIG: $milestone = 'xulrunner%version%';
|
||||
$milestone = 'xulrunner1.9.0.17';
|
||||
$notify_list = "build-announce\@mozilla.org";
|
||||
$stub_installer = 0;
|
||||
$sea_installer = 0;
|
||||
$archive = 1;
|
||||
$push_raw_xpis = 0;
|
||||
$crashreporter_buildsymbols = 0;
|
||||
$crashreporter_pushsymbols = 0;
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_HOST'} = '%symbolServer%';
|
||||
$ENV{'SYMBOL_SERVER_HOST'} = 'stage-old.mozilla.org';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_USER'} = '%symbolServerUser%';
|
||||
$ENV{'SYMBOL_SERVER_USER'} = 'xrbld';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_PATH'} = '%symbolServerPath%';
|
||||
$ENV{'SYMBOL_SERVER_PATH'} = '/mnt/netapp/breakpad/symbols_xr';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_SSH_KEY'} = '%symbolServerKey%';
|
||||
$ENV{'SYMBOL_SERVER_SSH_KEY'} = '/Users/cltbld/.ssh/xrbld_dsa';
|
||||
|
||||
# Reboot the OS at the end of build-and-test cycle. This is primarily
|
||||
# intended for Win9x, which can't last more than a few cycles before
|
||||
# locking up (and testing would be suspect even after a couple of cycles).
|
||||
# Right now, there is only code to force the reboot for Win9x, so even
|
||||
# setting this to 1, will not have an effect on other platforms. Setting
|
||||
# up win9x to automatically logon and begin running tinderbox is left
|
||||
# as an exercise to the reader.
|
||||
#$RebootSystem = 0;
|
||||
|
||||
# LogCompression specifies the type of compression used on the log file.
|
||||
# Valid options are 'gzip', and 'bzip2'. Please make sure the binaries
|
||||
# for 'gzip' or 'bzip2' are in the user's path before setting this
|
||||
# option.
|
||||
#$LogCompression = '';
|
||||
|
||||
# LogEncoding specifies the encoding format used for the logs. Valid
|
||||
# options are 'base64', and 'uuencode'. If $LogCompression is set above,
|
||||
# this needs to be set to 'base64' or 'uuencode' to ensure that the
|
||||
# binary data is transferred properly.
|
||||
#$LogEncoding = '';
|
||||
|
||||
# Prevent Extension Manager from spawning child processes during tests
|
||||
# - processes that tbox scripts cannot kill.
|
||||
#$ENV{NO_EM_RESTART} = '1';
|
||||
1
mozilla/tools/tinderbox-configs/xulrunner/win32/CLOBBER
Normal file
1
mozilla/tools/tinderbox-configs/xulrunner/win32/CLOBBER
Normal file
@@ -0,0 +1 @@
|
||||
Preemptive clobber for /README.txt merge conflict.
|
||||
18
mozilla/tools/tinderbox-configs/xulrunner/win32/mozconfig
Executable file
18
mozilla/tools/tinderbox-configs/xulrunner/win32/mozconfig
Executable file
@@ -0,0 +1,18 @@
|
||||
#
|
||||
# hostname: fxexp-win32-tbox
|
||||
# uname: CYGWIN_NT-5.2 fxexp-win32-tbox 1.5.19(0.150/4/2) 2006-01-20 13:28 i686 Cygwin
|
||||
#
|
||||
|
||||
export MOZILLA_OFFICIAL
|
||||
export JAVA_HOME=/d/jdk1.5.0_10
|
||||
|
||||
mk_add_options MOZILLA_OFFICIAL=1
|
||||
mk_add_options MOZ_CO_PROJECT=xulrunner
|
||||
mk_add_options MOZ_MAKE_FLAGS="-j2"
|
||||
|
||||
ac_add_options --enable-application=xulrunner
|
||||
ac_add_options --enable-jemalloc
|
||||
ac_add_options --disable-tests
|
||||
ac_add_options --enable-svg
|
||||
ac_add_options --enable-canvas
|
||||
ac_add_options --disable-installer
|
||||
255
mozilla/tools/tinderbox-configs/xulrunner/win32/tinder-config.pl
Executable file
255
mozilla/tools/tinderbox-configs/xulrunner/win32/tinder-config.pl
Executable file
@@ -0,0 +1,255 @@
|
||||
#
|
||||
# hostname: fxexp-win32-tbox
|
||||
# uname: CYGWIN_NT-5.2 fxexp-win32-tbox 1.5.19(0.150/4/2) 2006-01-20 13:28 i686 Cygwin
|
||||
#
|
||||
|
||||
#- tinder-config.pl - Tinderbox configuration file.
|
||||
#- Uncomment the variables you need to set.
|
||||
#- The default values are the same as the commented variables.
|
||||
|
||||
$ENV{NO_EM_RESTART} = "1";
|
||||
$ENV{MOZ_INSTALLER_USE_7ZIP} = "1";
|
||||
$ENV{CVS_RSH} = "ssh";
|
||||
$ENV{MOZ_CRASHREPORTER_NO_REPORT} = '1';
|
||||
# Both these two variables are for source server support
|
||||
$ENV{PDBSTR_PATH} = 'C:\\Program Files\\Debugging Tools for Windows\\sdk\\srcsrv\\pdbstr.exe';
|
||||
$ENV{SRCSRV_ROOT} = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot';
|
||||
|
||||
# $ENV{MOZ_PACKAGE_MSI}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: 0
|
||||
# Values: 0 | 1
|
||||
# Purpose: Controls whether a MSI package is made.
|
||||
# Requires: Windows and a local MakeMSI installation.
|
||||
#$ENV{MOZ_PACKAGE_MSI} = 0;
|
||||
|
||||
# $ENV{MOZ_SYMBOLS_TRANSFER_TYPE}
|
||||
#-----------------------------------------------------------------------------
|
||||
# Default: scp
|
||||
# Values: scp | rsync
|
||||
# Purpose: Use scp or rsync to transfer symbols to the Talkback server.
|
||||
# Requires: The selected type requires the command be available both locally
|
||||
# and on the Talkback server.
|
||||
#$ENV{MOZ_SYMBOLS_TRANSFER_TYPE} = "scp";
|
||||
|
||||
#- PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
|
||||
#$BuildAdministrator = "$ENV{USER}\@$ENV{HOST}";
|
||||
#$BuildAdministrator = ($ENV{USER} || "cltbld") . "\@" . ($ENV{HOST} || "dhcp");
|
||||
$BuildAdministrator = 'build@mozilla.org';
|
||||
|
||||
#- You'll need to change these to suit your machine's needs
|
||||
#$DisplayServer = ':0.0';
|
||||
|
||||
#- Default values of command-line opts
|
||||
#-
|
||||
#$BuildDepend = 1; # Depend or Clobber
|
||||
#$BuildDebug = 0; # Debug or Opt (Darwin)
|
||||
#$ReportStatus = 1; # Send results to server, or not
|
||||
#$ReportFinalStatus = 1; # Finer control over $ReportStatus.
|
||||
$UseTimeStamp = 0; # Use the CVS 'pull-by-timestamp' option, or not
|
||||
#$BuildOnce = 0; # Build once, don't send results to server
|
||||
#$TestOnly = 0; # Only run tests, don't pull/build
|
||||
#$BuildEmbed = 0; # After building seamonkey, go build embed app.
|
||||
#$SkipMozilla = 0; # Use to debug post-mozilla.pl scripts.
|
||||
#$BuildLocales = 0; # Do l10n packaging?
|
||||
$BuildSDK = 1; # Build the SDK
|
||||
|
||||
# Tests
|
||||
#$CleanProfile = 0;
|
||||
#$ResetHomeDirForTests = 1;
|
||||
$ProductName = "XULRunner";
|
||||
$VendorName = 'Mozilla';
|
||||
|
||||
$RunMozillaTests = 0; # Allow turning off of all tests if needed.
|
||||
#$RegxpcomTest = 1;
|
||||
#$AliveTest = 1;
|
||||
#$JavaTest = 0;
|
||||
#$ViewerTest = 0;
|
||||
#$BloatTest = 0; # warren memory bloat test
|
||||
#$BloatTest2 = 0; # dbaron memory bloat test, require tracemalloc
|
||||
#$DomToTextConversionTest = 0;
|
||||
#$XpcomGlueTest = 0;
|
||||
#$CodesizeTest = 0; # Z, require mozilla/tools/codesighs
|
||||
#$EmbedCodesizeTest = 0; # mZ, require mozilla/tools/codesigns
|
||||
#$MailBloatTest = 0;
|
||||
#$EmbedTest = 0; # Assumes you wanted $BuildEmbed=1
|
||||
#$LayoutPerformanceTest = 0; # Tp
|
||||
#$DHTMLPerformanceTest = 0; # Tdhtml
|
||||
#$QATest = 0;
|
||||
#$XULWindowOpenTest = 0; # Txul
|
||||
#$StartupPerformanceTest = 0; # Ts
|
||||
|
||||
#$TestsPhoneHome = 0; # Should test report back to server?
|
||||
|
||||
# $results_server
|
||||
#----------------------------------------------------------------------------
|
||||
# Server on which test results will be accessible. This was originally tegu,
|
||||
# then became axolotl. Once we moved services from axolotl, it was time
|
||||
# to give this service its own hostname to make future transitions easier.
|
||||
# - cmp@mozilla.org
|
||||
#$results_server = "build-graphs.mozilla.org";
|
||||
|
||||
#$pageload_server = "spider"; # localhost
|
||||
|
||||
#
|
||||
# Timeouts, values are in seconds.
|
||||
#
|
||||
#$CVSCheckoutTimeout = 3600;
|
||||
#$CreateProfileTimeout = 45;
|
||||
#$RegxpcomTestTimeout = 120;
|
||||
|
||||
#$AliveTestTimeout = 45;
|
||||
#$ViewerTestTimeout = 45;
|
||||
#$EmbedTestTimeout = 45;
|
||||
#$BloatTestTimeout = 120; # seconds
|
||||
#$MailBloatTestTimeout = 120; # seconds
|
||||
#$JavaTestTimeout = 45;
|
||||
#$DomTestTimeout = 45; # seconds
|
||||
#$XpcomGlueTestTimeout = 15;
|
||||
#$CodesizeTestTimeout = 900; # seconds
|
||||
#$CodesizeTestType = "auto"; # {"auto"|"base"}
|
||||
#$LayoutPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$DHTMLPerformanceTestTimeout = 1200; # entire test, seconds
|
||||
#$QATestTimeout = 1200; # entire test, seconds
|
||||
#$LayoutPerformanceTestPageTimeout = 30000; # each page, ms
|
||||
#$StartupPerformanceTestTimeout = 15; # seconds
|
||||
#$XULWindowOpenTestTimeout = 150; # seconds
|
||||
|
||||
|
||||
#$MozConfigFileName = 'mozconfig';
|
||||
|
||||
#$UseMozillaProfile = 1;
|
||||
#$MozProfileName = 'default';
|
||||
|
||||
#- Set these to what makes sense for your system
|
||||
$Make = 'make'; # Must be GNU make
|
||||
#$MakeOverrides = '';
|
||||
#$mail = '/bin/mail';
|
||||
#$CVS = 'cvs -q';
|
||||
#$CVSCO = 'checkout -P';
|
||||
|
||||
# win32 usually doesn't have /bin/mail
|
||||
$blat = '/d/mozilla-build/blat261/full/blat';
|
||||
$use_blat = 1;
|
||||
|
||||
# Set moz_cvsroot to something like:
|
||||
# :pserver:$ENV{USER}%netscape.com\@cvs.mozilla.org:/cvsroot
|
||||
# :pserver:anonymous\@cvs-mirror.mozilla.org:/cvsroot
|
||||
#
|
||||
# Note that win32 may not need \@, depends on ' or ".
|
||||
# :pserver:$ENV{USER}%netscape.com@cvs.mozilla.org:/cvsroot
|
||||
|
||||
#$moz_cvsroot = $ENV{CVSROOT};
|
||||
# CONFIG: $moz_cvsroot = '%mozillaCvsroot%';
|
||||
$moz_cvsroot = 'cltbld@cvs.mozilla.org:/cvsroot';
|
||||
|
||||
#- Set these proper values for your tinderbox server
|
||||
#$Tinderbox_server = 'tinderbox-daemon@tinderbox.mozilla.org';
|
||||
|
||||
# Allow for non-client builds, e.g. camino.
|
||||
#$moz_client_mk = 'client.mk';
|
||||
|
||||
#- Set if you want to build in a separate object tree
|
||||
$ObjDir = 'obj-xulrunner';
|
||||
|
||||
# Extra build name, if needed.
|
||||
$BuildNameExtra = 'Release';
|
||||
|
||||
# User comment, eg. ip address for dhcp builds.
|
||||
# ex: $UserComment = "ip = 208.12.36.108";
|
||||
#$UserComment = 0;
|
||||
|
||||
#-
|
||||
#- The rest should not need to be changed
|
||||
#-
|
||||
|
||||
#- Minimum wait period from start of build to start of next build in minutes.
|
||||
#$BuildSleep = 10;
|
||||
|
||||
#- Until you get the script working. When it works,
|
||||
#- change to the tree you're actually building
|
||||
# CONFIG: $BuildTree = '%buildTree%';
|
||||
$BuildTree = 'MozillaRelease';
|
||||
|
||||
#$BuildName = '';
|
||||
# CONFIG: $BuildTag = '%productTag%_RELEASE';
|
||||
$BuildTag = 'FIREFOX_3_0_17_RELEASE';
|
||||
#$BuildConfigDir = 'mozilla/config';
|
||||
#$Topsrcdir = 'mozilla';
|
||||
|
||||
$BinaryName = 'xulrunner.exe';
|
||||
|
||||
#
|
||||
# For embedding app, use:
|
||||
#$EmbedBinaryName = 'TestGtkEmbed';
|
||||
#$EmbedDistDir = 'dist/bin'
|
||||
|
||||
|
||||
#$ShellOverride = ''; # Only used if the default shell is too stupid
|
||||
#$ConfigureArgs = '';
|
||||
#$ConfigureEnvArgs = '';
|
||||
#$Compiler = 'gcc';
|
||||
#$NSPRArgs = '';
|
||||
#$ShellOverride = '';
|
||||
|
||||
# Release build options
|
||||
$ReleaseBuild = 1;
|
||||
$shiptalkback = 0;
|
||||
$ReleaseToLatest = 0; # Push the release to latest-<milestone>?
|
||||
$ReleaseToDated = 1; # Push the release to YYYY-MM-DD-HH-<milestone>?
|
||||
#$build_hour = "8";
|
||||
$package_creation_path = "/xulrunner/installer";
|
||||
# needs setting for mac + talkback: $mac_bundle_path = "/browser/app";
|
||||
$ssh_version = "2";
|
||||
# CONFIG: $ssh_user = "%sshUser%";
|
||||
$ssh_user = "cltbld";
|
||||
#$ssh_key = "'$ENV{HOME}/.ssh/xrbld_dsa'";
|
||||
# CONFIG: $ssh_server = "%sshServer%";
|
||||
$ssh_server = "stage-old.mozilla.org";
|
||||
$ReleaseGroup = "xulrunner";
|
||||
$ftp_path = "/home/ftp/pub/xulrunner/nightly";
|
||||
$url_path = "http://ftp.mozilla.org/pub/mozilla.org/xulrunner/nightly";
|
||||
$tbox_ftp_path = "/home/ftp/pub/xulrunner/tinderbox-builds";
|
||||
$tbox_url_path = "http://ftp.mozilla.org/pub/mozilla.org/xulrunner/tinderbox-builds";
|
||||
# CONFIG: $milestone = 'xulrunner%version%';
|
||||
$milestone = 'xulrunner1.9.0.17';
|
||||
$notify_list = 'build-announce@mozilla.org';
|
||||
$stub_installer = 0;
|
||||
$sea_installer = 0;
|
||||
$archive = 1;
|
||||
$push_raw_xpis = 0;
|
||||
$crashreporter_buildsymbols = 1;
|
||||
$crashreporter_pushsymbols = 1;
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_HOST'} = '%symbolServer%';
|
||||
$ENV{'SYMBOL_SERVER_HOST'} = 'stage-old.mozilla.org';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_USER'} = '%symbolServerUser%';
|
||||
$ENV{'SYMBOL_SERVER_USER'} = 'xrbld';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_PATH'} = '%symbolServerPath%';
|
||||
$ENV{'SYMBOL_SERVER_PATH'} = '/mnt/netapp/breakpad/symbols_xr';
|
||||
# CONFIG: $ENV{'SYMBOL_SERVER_SSH_KEY'} = '%symbolServerKey%';
|
||||
$ENV{'SYMBOL_SERVER_SSH_KEY'} = '/c/Documents and Settings/cltbld/.ssh/xrbld_dsa';
|
||||
|
||||
# Reboot the OS at the end of build-and-test cycle. This is primarily
|
||||
# intended for Win9x, which can't last more than a few cycles before
|
||||
# locking up (and testing would be suspect even after a couple of cycles).
|
||||
# Right now, there is only code to force the reboot for Win9x, so even
|
||||
# setting this to 1, will not have an effect on other platforms. Setting
|
||||
# up win9x to automatically logon and begin running tinderbox is left
|
||||
# as an exercise to the reader.
|
||||
#$RebootSystem = 0;
|
||||
|
||||
# LogCompression specifies the type of compression used on the log file.
|
||||
# Valid options are 'gzip', and 'bzip2'. Please make sure the binaries
|
||||
# for 'gzip' or 'bzip2' are in the user's path before setting this
|
||||
# option.
|
||||
#$LogCompression = '';
|
||||
|
||||
# LogEncoding specifies the encoding format used for the logs. Valid
|
||||
# options are 'base64', and 'uuencode'. If $LogCompression is set above,
|
||||
# this needs to be set to 'base64' or 'uuencode' to ensure that the
|
||||
# binary data is transferred properly.
|
||||
#$LogEncoding = '';
|
||||
|
||||
# Prevent Extension Manager from spawning child processes during tests
|
||||
# - processes that tbox scripts cannot kill.
|
||||
#$ENV{NO_EM_RESTART} = '1';
|
||||
@@ -1,32 +0,0 @@
|
||||
.htaccess
|
||||
/lib/*
|
||||
/template/en/custom
|
||||
/docs/bugzilla.ent
|
||||
/docs/en/xml/bugzilla.ent
|
||||
/docs/en/txt
|
||||
/docs/en/html
|
||||
/docs/en/pdf
|
||||
/skins/custom
|
||||
/graphs
|
||||
/data
|
||||
/localconfig
|
||||
/index.html
|
||||
|
||||
/skins/contrib/Dusk/IE-fixes.css
|
||||
/skins/contrib/Dusk/admin.css
|
||||
/skins/contrib/Dusk/attachment.css
|
||||
/skins/contrib/Dusk/create_attachment.css
|
||||
/skins/contrib/Dusk/dependency-tree.css
|
||||
/skins/contrib/Dusk/duplicates.css
|
||||
/skins/contrib/Dusk/editusers.css
|
||||
/skins/contrib/Dusk/enter_bug.css
|
||||
/skins/contrib/Dusk/help.css
|
||||
/skins/contrib/Dusk/panel.css
|
||||
/skins/contrib/Dusk/page.css
|
||||
/skins/contrib/Dusk/params.css
|
||||
/skins/contrib/Dusk/reports.css
|
||||
/skins/contrib/Dusk/show_bug.css
|
||||
/skins/contrib/Dusk/search_form.css
|
||||
/skins/contrib/Dusk/show_multiple.css
|
||||
/skins/contrib/Dusk/summarize-time.css
|
||||
.DS_Store
|
||||
@@ -1 +0,0 @@
|
||||
7819
|
||||
32
mozilla/webtools/bugzilla/.gitignore
vendored
32
mozilla/webtools/bugzilla/.gitignore
vendored
@@ -1,32 +0,0 @@
|
||||
.htaccess
|
||||
/lib/*
|
||||
/template/en/custom
|
||||
/docs/bugzilla.ent
|
||||
/docs/en/xml/bugzilla.ent
|
||||
/docs/en/txt
|
||||
/docs/en/html
|
||||
/docs/en/pdf
|
||||
/skins/custom
|
||||
/graphs
|
||||
/data
|
||||
/localconfig
|
||||
/index.html
|
||||
|
||||
/skins/contrib/Dusk/IE-fixes.css
|
||||
/skins/contrib/Dusk/admin.css
|
||||
/skins/contrib/Dusk/attachment.css
|
||||
/skins/contrib/Dusk/create_attachment.css
|
||||
/skins/contrib/Dusk/dependency-tree.css
|
||||
/skins/contrib/Dusk/duplicates.css
|
||||
/skins/contrib/Dusk/editusers.css
|
||||
/skins/contrib/Dusk/enter_bug.css
|
||||
/skins/contrib/Dusk/help.css
|
||||
/skins/contrib/Dusk/panel.css
|
||||
/skins/contrib/Dusk/page.css
|
||||
/skins/contrib/Dusk/params.css
|
||||
/skins/contrib/Dusk/reports.css
|
||||
/skins/contrib/Dusk/show_bug.css
|
||||
/skins/contrib/Dusk/search_form.css
|
||||
/skins/contrib/Dusk/show_multiple.css
|
||||
/skins/contrib/Dusk/summarize-time.css
|
||||
.DS_Store
|
||||
@@ -1 +0,0 @@
|
||||
f80ce0c0974010ba97b1467e764e5f2f239fe514
|
||||
@@ -1,25 +0,0 @@
|
||||
# Don't allow people to retrieve non-cgi executable files or our private data
|
||||
<FilesMatch (\.pm|\.pl|\.tmpl|localconfig.*)$>
|
||||
deny from all
|
||||
</FilesMatch>
|
||||
<IfModule mod_expires.c>
|
||||
<IfModule mod_headers.c>
|
||||
<IfModule mod_env.c>
|
||||
<FilesMatch (\.js|\.css)$>
|
||||
ExpiresActive On
|
||||
# According to RFC 2616, "1 year in the future" means "never expire".
|
||||
# We change the name of the file's URL whenever its modification date
|
||||
# changes, so browsers can cache any individual JS or CSS URL forever.
|
||||
# However, since all JS and CSS URLs involve a ? in them (for the changing
|
||||
# name) we have to explicitly set an Expires header or browsers won't
|
||||
# *ever* cache them.
|
||||
ExpiresDefault "now plus 1 years"
|
||||
Header append Cache-Control "public"
|
||||
</FilesMatch>
|
||||
|
||||
# This lets Bugzilla know that we are properly sending Cache-Control
|
||||
# and Expires headers for CSS and JS files.
|
||||
SetEnv BZ_CACHE_CONTROL 1
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
@@ -1,56 +0,0 @@
|
||||
language: perl
|
||||
|
||||
addons:
|
||||
postgresql: "9.1"
|
||||
|
||||
perl:
|
||||
- 5.10
|
||||
- 5.12
|
||||
|
||||
env:
|
||||
- TEST_SUITE=sanity
|
||||
- TEST_SUITE=docs
|
||||
- TEST_SUITE=webservices DB=mysql
|
||||
- TEST_SUITE=selenium DB=mysql
|
||||
- TEST_SUITE=webservices DB=pg
|
||||
- TEST_SUITE=selenium DB=pg
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- perl: 5.12
|
||||
env: TEST_SUITE=docs
|
||||
- perl: 5.10
|
||||
env: TEST_SUITE=webservices DB=mysql
|
||||
- perl: 5.12
|
||||
env: TEST_SUITE=selenium DB=mysql
|
||||
- perl: 5.10
|
||||
env: TEST_SUITE=webservices DB=pg
|
||||
- perl: 5.12
|
||||
env: TEST_SUITE=selenium DB=pg
|
||||
|
||||
before_install:
|
||||
- git clone https://github.com/bugzilla/qa.git -b 4.0 qa
|
||||
|
||||
install: true
|
||||
|
||||
before_script:
|
||||
- mysql -u root mysql -e "GRANT ALL PRIVILEGES ON *.* TO bugs@localhost IDENTIFIED BY 'bugs'; FLUSH PRIVILEGES;"
|
||||
- psql -c "CREATE USER bugs WITH PASSWORD 'bugs' CREATEDB;" -U postgres
|
||||
|
||||
script: ./qa/travis.sh
|
||||
|
||||
after_failure:
|
||||
- sudo cat /var/log/apache2/error.log
|
||||
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "irc.mozilla.org#qa-bugzilla"
|
||||
- "irc.mozilla.org#bugzilla"
|
||||
template:
|
||||
- "Bugzilla %{branch} : %{author} : %{message}"
|
||||
- "Commit Message : %{commit_message}"
|
||||
- "Commit Link : %{compare_url}"
|
||||
- "Build Link : %{build_url}"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
@@ -1,871 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# A. Karl Kornel <karl@kornel.name>
|
||||
# Marc Schumann <wurblzap@gmail.com>
|
||||
|
||||
package Bugzilla;
|
||||
|
||||
use strict;
|
||||
|
||||
# We want any compile errors to get to the browser, if possible.
|
||||
BEGIN {
|
||||
# This makes sure we're in a CGI.
|
||||
if ($ENV{SERVER_SOFTWARE} && !$ENV{MOD_PERL}) {
|
||||
require CGI::Carp;
|
||||
CGI::Carp->import('fatalsToBrowser');
|
||||
}
|
||||
}
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Auth;
|
||||
use Bugzilla::Auth::Persist::Cookie;
|
||||
use Bugzilla::CGI;
|
||||
use Bugzilla::Extension;
|
||||
use Bugzilla::DB;
|
||||
use Bugzilla::Install::Localconfig qw(read_localconfig);
|
||||
use Bugzilla::Install::Requirements qw(OPTIONAL_MODULES);
|
||||
use Bugzilla::Install::Util qw(init_console);
|
||||
use Bugzilla::Template;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Field;
|
||||
use Bugzilla::Flag;
|
||||
use Bugzilla::Token;
|
||||
|
||||
use File::Basename;
|
||||
use File::Spec::Functions;
|
||||
use DateTime::TimeZone;
|
||||
use Date::Parse;
|
||||
use Safe;
|
||||
|
||||
#####################################################################
|
||||
# Constants
|
||||
#####################################################################
|
||||
|
||||
# Scripts that are not stopped by shutdownhtml being in effect.
|
||||
use constant SHUTDOWNHTML_EXEMPT => qw(
|
||||
editparams.cgi
|
||||
checksetup.pl
|
||||
migrate.pl
|
||||
recode.pl
|
||||
);
|
||||
|
||||
# Non-cgi scripts that should silently exit.
|
||||
use constant SHUTDOWNHTML_EXIT_SILENTLY => qw(
|
||||
whine.pl
|
||||
);
|
||||
|
||||
#####################################################################
|
||||
# Global Code
|
||||
#####################################################################
|
||||
|
||||
# $::SIG{__DIE__} = i_am_cgi() ? \&CGI::Carp::confess : \&Carp::confess;
|
||||
|
||||
# Note that this is a raw subroutine, not a method, so $class isn't available.
|
||||
sub init_page {
|
||||
if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
|
||||
init_console();
|
||||
}
|
||||
elsif (Bugzilla->params->{'utf8'}) {
|
||||
binmode STDOUT, ':utf8';
|
||||
}
|
||||
|
||||
if (${^TAINT}) {
|
||||
# Some environment variables are not taint safe
|
||||
delete @::ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
|
||||
# Some modules throw undefined errors (notably File::Spec::Win32) if
|
||||
# PATH is undefined.
|
||||
$ENV{'PATH'} = '';
|
||||
}
|
||||
|
||||
# Because this function is run live from perl "use" commands of
|
||||
# other scripts, we're skipping the rest of this function if we get here
|
||||
# during a perl syntax check (perl -c, like we do during the
|
||||
# 001compile.t test).
|
||||
return if $^C;
|
||||
|
||||
# IIS prints out warnings to the webpage, so ignore them, or log them
|
||||
# to a file if the file exists.
|
||||
if ($ENV{SERVER_SOFTWARE} && $ENV{SERVER_SOFTWARE} =~ /microsoft-iis/i) {
|
||||
$SIG{__WARN__} = sub {
|
||||
my ($msg) = @_;
|
||||
my $datadir = bz_locations()->{'datadir'};
|
||||
if (-w "$datadir/errorlog") {
|
||||
my $warning_log = new IO::File(">>$datadir/errorlog");
|
||||
print $warning_log $msg;
|
||||
$warning_log->close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
my $script = basename($0);
|
||||
|
||||
# Because of attachment_base, attachment.cgi handles this itself.
|
||||
if ($script ne 'attachment.cgi') {
|
||||
do_ssl_redirect_if_required();
|
||||
}
|
||||
|
||||
# If Bugzilla is shut down, do not allow anything to run, just display a
|
||||
# message to the user about the downtime and log out. Scripts listed in
|
||||
# SHUTDOWNHTML_EXEMPT are exempt from this message.
|
||||
#
|
||||
# This code must go here. It cannot go anywhere in Bugzilla::CGI, because
|
||||
# it uses Template, and that causes various dependency loops.
|
||||
if (Bugzilla->params->{"shutdownhtml"}
|
||||
&& !grep { $_ eq $script } SHUTDOWNHTML_EXEMPT)
|
||||
{
|
||||
# Allow non-cgi scripts to exit silently (without displaying any
|
||||
# message), if desired. At this point, no DBI call has been made
|
||||
# yet, and no error will be returned if the DB is inaccessible.
|
||||
if (!i_am_cgi()
|
||||
&& grep { $_ eq $script } SHUTDOWNHTML_EXIT_SILENTLY)
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
# For security reasons, log out users when Bugzilla is down.
|
||||
# Bugzilla->login() is required to catch the logincookie, if any.
|
||||
my $user;
|
||||
eval { $user = Bugzilla->login(LOGIN_OPTIONAL); };
|
||||
if ($@) {
|
||||
# The DB is not accessible. Use the default user object.
|
||||
$user = Bugzilla->user;
|
||||
$user->{settings} = {};
|
||||
}
|
||||
my $userid = $user->id;
|
||||
Bugzilla->logout();
|
||||
|
||||
my $template = Bugzilla->template;
|
||||
my $vars = {};
|
||||
$vars->{'message'} = 'shutdown';
|
||||
$vars->{'userid'} = $userid;
|
||||
# Generate and return a message about the downtime, appropriately
|
||||
# for if we're a command-line script or a CGI script.
|
||||
my $extension;
|
||||
if (i_am_cgi() && (!Bugzilla->cgi->param('ctype')
|
||||
|| Bugzilla->cgi->param('ctype') eq 'html')) {
|
||||
$extension = 'html';
|
||||
}
|
||||
else {
|
||||
$extension = 'txt';
|
||||
}
|
||||
print Bugzilla->cgi->header() if i_am_cgi();
|
||||
my $t_output;
|
||||
$template->process("global/message.$extension.tmpl", $vars, \$t_output)
|
||||
|| ThrowTemplateError($template->error);
|
||||
print $t_output . "\n";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# Subroutines and Methods
|
||||
#####################################################################
|
||||
|
||||
sub template {
|
||||
my $class = shift;
|
||||
$class->request_cache->{template} ||= Bugzilla::Template->create();
|
||||
return $class->request_cache->{template};
|
||||
}
|
||||
|
||||
sub template_inner {
|
||||
my ($class, $lang) = @_;
|
||||
my $cache = $class->request_cache;
|
||||
my $current_lang = $cache->{template_current_lang}->[0];
|
||||
$lang ||= $current_lang || '';
|
||||
$class->request_cache->{"template_inner_$lang"}
|
||||
||= Bugzilla::Template->create(language => $lang);
|
||||
return $class->request_cache->{"template_inner_$lang"};
|
||||
}
|
||||
|
||||
our $extension_packages;
|
||||
sub extensions {
|
||||
my ($class) = @_;
|
||||
my $cache = $class->request_cache;
|
||||
if (!$cache->{extensions}) {
|
||||
# Under mod_perl, mod_perl.pl populates $extension_packages for us.
|
||||
if (!$extension_packages) {
|
||||
$extension_packages = Bugzilla::Extension->load_all();
|
||||
}
|
||||
my @extensions;
|
||||
foreach my $package (@$extension_packages) {
|
||||
my $extension = $package->new();
|
||||
if ($extension->enabled) {
|
||||
push(@extensions, $extension);
|
||||
}
|
||||
}
|
||||
$cache->{extensions} = \@extensions;
|
||||
}
|
||||
return $cache->{extensions};
|
||||
}
|
||||
|
||||
sub feature {
|
||||
my ($class, $feature) = @_;
|
||||
my $cache = $class->request_cache;
|
||||
return $cache->{feature}->{$feature}
|
||||
if exists $cache->{feature}->{$feature};
|
||||
|
||||
my $feature_map = $cache->{feature_map};
|
||||
if (!$feature_map) {
|
||||
foreach my $package (@{ OPTIONAL_MODULES() }) {
|
||||
foreach my $f (@{ $package->{feature} }) {
|
||||
$feature_map->{$f} ||= [];
|
||||
push(@{ $feature_map->{$f} }, $package->{module});
|
||||
}
|
||||
}
|
||||
$cache->{feature_map} = $feature_map;
|
||||
}
|
||||
|
||||
if (!$feature_map->{$feature}) {
|
||||
ThrowCodeError('invalid_feature', { feature => $feature });
|
||||
}
|
||||
|
||||
my $success = 1;
|
||||
foreach my $module (@{ $feature_map->{$feature} }) {
|
||||
# We can't use a string eval and "use" here (it kills Template-Toolkit,
|
||||
# see https://rt.cpan.org/Public/Bug/Display.html?id=47929), so we have
|
||||
# to do a block eval.
|
||||
$module =~ s{::}{/}g;
|
||||
$module .= ".pm";
|
||||
eval { require $module; 1; } or $success = 0;
|
||||
}
|
||||
$cache->{feature}->{$feature} = $success;
|
||||
return $success;
|
||||
}
|
||||
|
||||
sub cgi {
|
||||
my $class = shift;
|
||||
$class->request_cache->{cgi} ||= new Bugzilla::CGI();
|
||||
return $class->request_cache->{cgi};
|
||||
}
|
||||
|
||||
sub input_params {
|
||||
my ($class, $params) = @_;
|
||||
my $cache = $class->request_cache;
|
||||
# This is how the WebService and other places set input_params.
|
||||
if (defined $params) {
|
||||
$cache->{input_params} = $params;
|
||||
}
|
||||
return $cache->{input_params} if defined $cache->{input_params};
|
||||
|
||||
# Making this scalar makes it a tied hash to the internals of $cgi,
|
||||
# so if a variable is changed, then it actually changes the $cgi object
|
||||
# as well.
|
||||
$cache->{input_params} = $class->cgi->Vars;
|
||||
return $cache->{input_params};
|
||||
}
|
||||
|
||||
sub localconfig {
|
||||
my $class = shift;
|
||||
$class->request_cache->{localconfig} ||= read_localconfig();
|
||||
return $class->request_cache->{localconfig};
|
||||
}
|
||||
|
||||
sub params {
|
||||
my $class = shift;
|
||||
$class->request_cache->{params} ||= Bugzilla::Config::read_param_file();
|
||||
return $class->request_cache->{params};
|
||||
}
|
||||
|
||||
sub user {
|
||||
my $class = shift;
|
||||
$class->request_cache->{user} ||= new Bugzilla::User;
|
||||
return $class->request_cache->{user};
|
||||
}
|
||||
|
||||
sub set_user {
|
||||
my ($class, $user) = @_;
|
||||
$class->request_cache->{user} = $user;
|
||||
}
|
||||
|
||||
sub sudoer {
|
||||
my $class = shift;
|
||||
return $class->request_cache->{sudoer};
|
||||
}
|
||||
|
||||
sub sudo_request {
|
||||
my ($class, $new_user, $new_sudoer) = @_;
|
||||
$class->request_cache->{user} = $new_user;
|
||||
$class->request_cache->{sudoer} = $new_sudoer;
|
||||
# NOTE: If you want to log the start of an sudo session, do it here.
|
||||
}
|
||||
|
||||
sub page_requires_login {
|
||||
return $_[0]->request_cache->{page_requires_login};
|
||||
}
|
||||
|
||||
sub login {
|
||||
my ($class, $type) = @_;
|
||||
|
||||
return $class->user if $class->user->id;
|
||||
|
||||
my $authorizer = new Bugzilla::Auth();
|
||||
$type = LOGIN_REQUIRED if $class->cgi->param('GoAheadAndLogIn');
|
||||
|
||||
if (!defined $type || $type == LOGIN_NORMAL) {
|
||||
$type = $class->params->{'requirelogin'} ? LOGIN_REQUIRED : LOGIN_NORMAL;
|
||||
}
|
||||
|
||||
# Allow templates to know that we're in a page that always requires
|
||||
# login.
|
||||
if ($type == LOGIN_REQUIRED) {
|
||||
$class->request_cache->{page_requires_login} = 1;
|
||||
}
|
||||
|
||||
my $authenticated_user = $authorizer->login($type);
|
||||
|
||||
# At this point, we now know if a real person is logged in.
|
||||
# We must now check to see if an sudo session is in progress.
|
||||
# For a session to be in progress, the following must be true:
|
||||
# 1: There must be a logged in user
|
||||
# 2: That user must be in the 'bz_sudoer' group
|
||||
# 3: There must be a valid value in the 'sudo' cookie
|
||||
# 4: A Bugzilla::User object must exist for the given cookie value
|
||||
# 5: That user must NOT be in the 'bz_sudo_protect' group
|
||||
my $token = $class->cgi->cookie('sudo');
|
||||
if (defined $authenticated_user && $token) {
|
||||
my ($user_id, $date, $sudo_target_id) = Bugzilla::Token::GetTokenData($token);
|
||||
if (!$user_id
|
||||
|| $user_id != $authenticated_user->id
|
||||
|| !detaint_natural($sudo_target_id)
|
||||
|| (time() - str2time($date) > MAX_SUDO_TOKEN_AGE))
|
||||
{
|
||||
$class->cgi->remove_cookie('sudo');
|
||||
ThrowUserError('sudo_invalid_cookie');
|
||||
}
|
||||
|
||||
my $sudo_target = new Bugzilla::User($sudo_target_id);
|
||||
if ($authenticated_user->in_group('bz_sudoers')
|
||||
&& defined $sudo_target
|
||||
&& !$sudo_target->in_group('bz_sudo_protect'))
|
||||
{
|
||||
$class->set_user($sudo_target);
|
||||
$class->request_cache->{sudoer} = $authenticated_user;
|
||||
# And make sure that both users have the same Auth object,
|
||||
# since we never call Auth::login for the sudo target.
|
||||
$sudo_target->set_authorizer($authenticated_user->authorizer);
|
||||
|
||||
# NOTE: If you want to do any special logging, do it here.
|
||||
}
|
||||
else {
|
||||
delete_token($token);
|
||||
$class->cgi->remove_cookie('sudo');
|
||||
ThrowUserError('sudo_illegal_action', { sudoer => $authenticated_user,
|
||||
target_user => $sudo_target });
|
||||
}
|
||||
}
|
||||
else {
|
||||
$class->set_user($authenticated_user);
|
||||
}
|
||||
|
||||
return $class->user;
|
||||
}
|
||||
|
||||
sub logout {
|
||||
my ($class, $option) = @_;
|
||||
|
||||
# If we're not logged in, go away
|
||||
return unless $class->user->id;
|
||||
|
||||
$option = LOGOUT_CURRENT unless defined $option;
|
||||
Bugzilla::Auth::Persist::Cookie->logout({type => $option});
|
||||
$class->logout_request() unless $option eq LOGOUT_KEEP_CURRENT;
|
||||
}
|
||||
|
||||
sub logout_user {
|
||||
my ($class, $user) = @_;
|
||||
# When we're logging out another user we leave cookies alone, and
|
||||
# therefore avoid calling Bugzilla->logout() directly.
|
||||
Bugzilla::Auth::Persist::Cookie->logout({user => $user});
|
||||
}
|
||||
|
||||
# just a compatibility front-end to logout_user that gets a user by id
|
||||
sub logout_user_by_id {
|
||||
my ($class, $id) = @_;
|
||||
my $user = new Bugzilla::User($id);
|
||||
$class->logout_user($user);
|
||||
}
|
||||
|
||||
# hack that invalidates credentials for a single request
|
||||
sub logout_request {
|
||||
my $class = shift;
|
||||
delete $class->request_cache->{user};
|
||||
delete $class->request_cache->{sudoer};
|
||||
# We can't delete from $cgi->cookie, so logincookie data will remain
|
||||
# there. Don't rely on it: use Bugzilla->user->login instead!
|
||||
}
|
||||
|
||||
sub job_queue {
|
||||
my $class = shift;
|
||||
require Bugzilla::JobQueue;
|
||||
$class->request_cache->{job_queue} ||= Bugzilla::JobQueue->new();
|
||||
return $class->request_cache->{job_queue};
|
||||
}
|
||||
|
||||
sub dbh {
|
||||
my $class = shift;
|
||||
# If we're not connected, then we must want the main db
|
||||
$class->request_cache->{dbh} ||= $class->dbh_main;
|
||||
|
||||
return $class->request_cache->{dbh};
|
||||
}
|
||||
|
||||
sub dbh_main {
|
||||
my $class = shift;
|
||||
$class->request_cache->{dbh_main} ||= Bugzilla::DB::connect_main();
|
||||
return $class->request_cache->{dbh_main};
|
||||
}
|
||||
|
||||
sub languages {
|
||||
my $class = shift;
|
||||
return Bugzilla::Install::Util::supported_languages();
|
||||
}
|
||||
|
||||
sub error_mode {
|
||||
my ($class, $newval) = @_;
|
||||
if (defined $newval) {
|
||||
$class->request_cache->{error_mode} = $newval;
|
||||
}
|
||||
return $class->request_cache->{error_mode}
|
||||
|| (i_am_cgi() ? ERROR_MODE_WEBPAGE : ERROR_MODE_DIE);
|
||||
}
|
||||
|
||||
# This is used only by Bugzilla::Error to throw errors.
|
||||
sub _json_server {
|
||||
my ($class, $newval) = @_;
|
||||
if (defined $newval) {
|
||||
$class->request_cache->{_json_server} = $newval;
|
||||
}
|
||||
return $class->request_cache->{_json_server};
|
||||
}
|
||||
|
||||
sub usage_mode {
|
||||
my ($class, $newval) = @_;
|
||||
if (defined $newval) {
|
||||
if ($newval == USAGE_MODE_BROWSER) {
|
||||
$class->error_mode(ERROR_MODE_WEBPAGE);
|
||||
}
|
||||
elsif ($newval == USAGE_MODE_CMDLINE) {
|
||||
$class->error_mode(ERROR_MODE_DIE);
|
||||
}
|
||||
elsif ($newval == USAGE_MODE_XMLRPC) {
|
||||
$class->error_mode(ERROR_MODE_DIE_SOAP_FAULT);
|
||||
}
|
||||
elsif ($newval == USAGE_MODE_JSON) {
|
||||
$class->error_mode(ERROR_MODE_JSON_RPC);
|
||||
}
|
||||
elsif ($newval == USAGE_MODE_EMAIL) {
|
||||
$class->error_mode(ERROR_MODE_DIE);
|
||||
}
|
||||
elsif ($newval == USAGE_MODE_TEST) {
|
||||
$class->error_mode(ERROR_MODE_TEST);
|
||||
}
|
||||
else {
|
||||
ThrowCodeError('usage_mode_invalid',
|
||||
{'invalid_usage_mode', $newval});
|
||||
}
|
||||
$class->request_cache->{usage_mode} = $newval;
|
||||
}
|
||||
return $class->request_cache->{usage_mode}
|
||||
|| (i_am_cgi()? USAGE_MODE_BROWSER : USAGE_MODE_CMDLINE);
|
||||
}
|
||||
|
||||
sub installation_mode {
|
||||
my ($class, $newval) = @_;
|
||||
($class->request_cache->{installation_mode} = $newval) if defined $newval;
|
||||
return $class->request_cache->{installation_mode}
|
||||
|| INSTALLATION_MODE_INTERACTIVE;
|
||||
}
|
||||
|
||||
sub installation_answers {
|
||||
my ($class, $filename) = @_;
|
||||
if ($filename) {
|
||||
my $s = new Safe;
|
||||
$s->rdo($filename);
|
||||
|
||||
die "Error reading $filename: $!" if $!;
|
||||
die "Error evaluating $filename: $@" if $@;
|
||||
|
||||
# Now read the param back out from the sandbox
|
||||
$class->request_cache->{installation_answers} = $s->varglob('answer');
|
||||
}
|
||||
return $class->request_cache->{installation_answers} || {};
|
||||
}
|
||||
|
||||
sub switch_to_shadow_db {
|
||||
my $class = shift;
|
||||
|
||||
if (!$class->request_cache->{dbh_shadow}) {
|
||||
if ($class->params->{'shadowdb'}) {
|
||||
$class->request_cache->{dbh_shadow} = Bugzilla::DB::connect_shadow();
|
||||
} else {
|
||||
$class->request_cache->{dbh_shadow} = $class->dbh_main;
|
||||
}
|
||||
}
|
||||
|
||||
$class->request_cache->{dbh} = $class->request_cache->{dbh_shadow};
|
||||
# we have to return $class->dbh instead of {dbh} as
|
||||
# {dbh_shadow} may be undefined if no shadow DB is used
|
||||
# and no connection to the main DB has been established yet.
|
||||
return $class->dbh;
|
||||
}
|
||||
|
||||
sub switch_to_main_db {
|
||||
my $class = shift;
|
||||
|
||||
$class->request_cache->{dbh} = $class->dbh_main;
|
||||
return $class->dbh_main;
|
||||
}
|
||||
|
||||
sub get_fields {
|
||||
my $class = shift;
|
||||
my $criteria = shift;
|
||||
# This function may be called during installation, and Field::match
|
||||
# may fail at that time. so we want to return an empty list in that
|
||||
# case.
|
||||
my $fields = eval { Bugzilla::Field->match($criteria) } || [];
|
||||
return @$fields;
|
||||
}
|
||||
|
||||
sub active_custom_fields {
|
||||
my $class = shift;
|
||||
if (!exists $class->request_cache->{active_custom_fields}) {
|
||||
$class->request_cache->{active_custom_fields} =
|
||||
Bugzilla::Field->match({ custom => 1, obsolete => 0 });
|
||||
}
|
||||
return @{$class->request_cache->{active_custom_fields}};
|
||||
}
|
||||
|
||||
sub has_flags {
|
||||
my $class = shift;
|
||||
|
||||
if (!defined $class->request_cache->{has_flags}) {
|
||||
$class->request_cache->{has_flags} = Bugzilla::Flag->any_exist;
|
||||
}
|
||||
return $class->request_cache->{has_flags};
|
||||
}
|
||||
|
||||
sub local_timezone {
|
||||
my $class = shift;
|
||||
|
||||
if (!defined $class->request_cache->{local_timezone}) {
|
||||
$class->request_cache->{local_timezone} =
|
||||
DateTime::TimeZone->new(name => 'local');
|
||||
}
|
||||
return $class->request_cache->{local_timezone};
|
||||
}
|
||||
|
||||
# This creates the request cache for non-mod_perl installations.
|
||||
# This is identical to Install::Util::_cache so that things loaded
|
||||
# into Install::Util::_cache during installation can be read out
|
||||
# of request_cache later in installation.
|
||||
our $_request_cache = $Bugzilla::Install::Util::_cache;
|
||||
|
||||
sub request_cache {
|
||||
if ($ENV{MOD_PERL}) {
|
||||
require Apache2::RequestUtil;
|
||||
# Sometimes (for example, during mod_perl.pl), the request
|
||||
# object isn't available, and we should use $_request_cache instead.
|
||||
my $request = eval { Apache2::RequestUtil->request };
|
||||
return $_request_cache if !$request;
|
||||
return $request->pnotes();
|
||||
}
|
||||
return $_request_cache;
|
||||
}
|
||||
|
||||
# Private methods
|
||||
|
||||
# Per-process cleanup. Note that this is a plain subroutine, not a method,
|
||||
# so we don't have $class available.
|
||||
sub _cleanup {
|
||||
my $main = Bugzilla->request_cache->{dbh_main};
|
||||
my $shadow = Bugzilla->request_cache->{dbh_shadow};
|
||||
foreach my $dbh ($main, $shadow) {
|
||||
next if !$dbh;
|
||||
$dbh->bz_rollback_transaction() if $dbh->bz_in_transaction;
|
||||
$dbh->disconnect;
|
||||
}
|
||||
undef $_request_cache;
|
||||
}
|
||||
|
||||
sub END {
|
||||
# Bugzilla.pm cannot compile in mod_perl.pl if this runs.
|
||||
_cleanup() unless $ENV{MOD_PERL};
|
||||
}
|
||||
|
||||
init_page() if !$ENV{MOD_PERL};
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla - Semi-persistent collection of various objects used by scripts
|
||||
and modules
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla;
|
||||
|
||||
sub someModulesSub {
|
||||
Bugzilla->dbh->prepare(...);
|
||||
Bugzilla->template->process(...);
|
||||
}
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Several Bugzilla 'things' are used by a variety of modules and scripts. This
|
||||
includes database handles, template objects, and so on.
|
||||
|
||||
This module is a singleton intended as a central place to store these objects.
|
||||
This approach has several advantages:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
They're not global variables, so we don't have issues with them staying around
|
||||
with mod_perl
|
||||
|
||||
=item *
|
||||
|
||||
Everything is in one central place, so it's easy to access, modify, and maintain
|
||||
|
||||
=item *
|
||||
|
||||
Code in modules can get access to these objects without having to have them
|
||||
all passed from the caller, and the caller's caller, and....
|
||||
|
||||
=item *
|
||||
|
||||
We can reuse objects across requests using mod_perl where appropriate (eg
|
||||
templates), whilst destroying those which are only valid for a single request
|
||||
(such as the current user)
|
||||
|
||||
=back
|
||||
|
||||
Note that items accessible via this object are demand-loaded when requested.
|
||||
|
||||
For something to be added to this object, it should either be able to benefit
|
||||
from persistence when run under mod_perl (such as the a C<template> object),
|
||||
or should be something which is globally required by a large ammount of code
|
||||
(such as the current C<user> object).
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
Note that all C<Bugzilla> functionality is method based; use C<Bugzilla-E<gt>dbh>
|
||||
rather than C<Bugzilla::dbh>. Nothing cares about this now, but don't rely on
|
||||
that.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<template>
|
||||
|
||||
The current C<Template> object, to be used for output
|
||||
|
||||
=item C<template_inner>
|
||||
|
||||
If you ever need a L<Bugzilla::Template> object while you're already
|
||||
processing a template, use this. Also use it if you want to specify
|
||||
the language to use. If no argument is passed, it uses the last
|
||||
language set. If the argument is "" (empty string), the language is
|
||||
reset to the current one (the one used by Bugzilla->template).
|
||||
|
||||
=item C<cgi>
|
||||
|
||||
The current C<cgi> object. Note that modules should B<not> be using this in
|
||||
general. Not all Bugzilla actions are cgi requests. Its useful as a convenience
|
||||
method for those scripts/templates which are only use via CGI, though.
|
||||
|
||||
=item C<input_params>
|
||||
|
||||
When running under the WebService, this is a hashref containing the arguments
|
||||
passed to the WebService method that was called. When running in a normal
|
||||
script, this is a hashref containing the contents of the CGI parameters.
|
||||
|
||||
Modifying this hashref will modify the CGI parameters or the WebService
|
||||
arguments (depending on what C<input_params> currently represents).
|
||||
|
||||
This should be used instead of L</cgi> in situations where your code
|
||||
could be being called by either a normal CGI script or a WebService method,
|
||||
such as during a code hook.
|
||||
|
||||
B<Note:> When C<input_params> represents the CGI parameters, any
|
||||
parameter specified more than once (like C<foo=bar&foo=baz>) will appear
|
||||
as an arrayref in the hash, but any value specified only once will appear
|
||||
as a scalar. This means that even if a value I<can> appear multiple times,
|
||||
if it only I<does> appear once, then it will be a scalar in C<input_params>,
|
||||
not an arrayref.
|
||||
|
||||
=item C<user>
|
||||
|
||||
C<undef> if there is no currently logged in user or if the login code has not
|
||||
yet been run. If an sudo session is in progress, the C<Bugzilla::User>
|
||||
corresponding to the person who is being impersonated. If no session is in
|
||||
progress, the current C<Bugzilla::User>.
|
||||
|
||||
=item C<set_user>
|
||||
|
||||
Allows you to directly set what L</user> will return. You can use this
|
||||
if you want to bypass L</login> for some reason and directly "log in"
|
||||
a specific L<Bugzilla::User>. Be careful with it, though!
|
||||
|
||||
=item C<sudoer>
|
||||
|
||||
C<undef> if there is no currently logged in user, the currently logged in user
|
||||
is not in the I<sudoer> group, or there is no session in progress. If an sudo
|
||||
session is in progress, returns the C<Bugzilla::User> object corresponding to
|
||||
the person who logged in and initiated the session. If no session is in
|
||||
progress, returns the C<Bugzilla::User> object corresponding to the currently
|
||||
logged in user.
|
||||
|
||||
=item C<sudo_request>
|
||||
This begins an sudo session for the current request. It is meant to be
|
||||
used when a session has just started. For normal use, sudo access should
|
||||
normally be set at login time.
|
||||
|
||||
=item C<login>
|
||||
|
||||
Logs in a user, returning a C<Bugzilla::User> object, or C<undef> if there is
|
||||
no logged in user. See L<Bugzilla::Auth|Bugzilla::Auth>, and
|
||||
L<Bugzilla::User|Bugzilla::User>.
|
||||
|
||||
=item C<page_requires_login>
|
||||
|
||||
If the current page always requires the user to log in (for example,
|
||||
C<enter_bug.cgi> or any page called with C<?GoAheadAndLogIn=1>) then
|
||||
this will return something true. Otherwise it will return false. (This is
|
||||
set when you call L</login>.)
|
||||
|
||||
=item C<logout($option)>
|
||||
|
||||
Logs out the current user, which involves invalidating user sessions and
|
||||
cookies. Three options are available from
|
||||
L<Bugzilla::Constants|Bugzilla::Constants>: LOGOUT_CURRENT (the
|
||||
default), LOGOUT_ALL or LOGOUT_KEEP_CURRENT.
|
||||
|
||||
=item C<logout_user($user)>
|
||||
|
||||
Logs out the specified user (invalidating all his sessions), taking a
|
||||
Bugzilla::User instance.
|
||||
|
||||
=item C<logout_by_id($id)>
|
||||
|
||||
Logs out the user with the id specified. This is a compatibility
|
||||
function to be used in callsites where there is only a userid and no
|
||||
Bugzilla::User instance.
|
||||
|
||||
=item C<logout_request>
|
||||
|
||||
Essentially, causes calls to C<Bugzilla-E<gt>user> to return C<undef>. This has the
|
||||
effect of logging out a user for the current request only; cookies and
|
||||
database sessions are left intact.
|
||||
|
||||
=item C<error_mode>
|
||||
|
||||
Call either C<Bugzilla->error_mode(Bugzilla::Constants::ERROR_MODE_DIE)>
|
||||
or C<Bugzilla->error_mode(Bugzilla::Constants::ERROR_MODE_DIE_SOAP_FAULT)> to
|
||||
change this flag's default of C<Bugzilla::Constants::ERROR_MODE_WEBPAGE> and to
|
||||
indicate that errors should be passed to error mode specific error handlers
|
||||
rather than being sent to a browser and finished with an exit().
|
||||
|
||||
This is useful, for example, to keep C<eval> blocks from producing wild HTML
|
||||
on errors, making it easier for you to catch them.
|
||||
(Remember to reset the error mode to its previous value afterwards, though.)
|
||||
|
||||
C<Bugzilla->error_mode> will return the current state of this flag.
|
||||
|
||||
Note that C<Bugzilla->error_mode> is being called by C<Bugzilla->usage_mode> on
|
||||
usage mode changes.
|
||||
|
||||
=item C<usage_mode>
|
||||
|
||||
Call either C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_CMDLINE)>
|
||||
or C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_XMLRPC)> near the
|
||||
beginning of your script to change this flag's default of
|
||||
C<Bugzilla::Constants::USAGE_MODE_BROWSER> and to indicate that Bugzilla is
|
||||
being called in a non-interactive manner.
|
||||
|
||||
This influences error handling because on usage mode changes, C<usage_mode>
|
||||
calls C<Bugzilla->error_mode> to set an error mode which makes sense for the
|
||||
usage mode.
|
||||
|
||||
C<Bugzilla->usage_mode> will return the current state of this flag.
|
||||
|
||||
=item C<installation_mode>
|
||||
|
||||
Determines whether or not installation should be silent. See
|
||||
L<Bugzilla::Constants> for the C<INSTALLATION_MODE> constants.
|
||||
|
||||
=item C<installation_answers>
|
||||
|
||||
Returns a hashref representing any "answers" file passed to F<checksetup.pl>,
|
||||
used to automatically answer or skip prompts.
|
||||
|
||||
=item C<dbh>
|
||||
|
||||
The current database handle. See L<DBI>.
|
||||
|
||||
=item C<dbh_main>
|
||||
|
||||
The main database handle. See L<DBI>.
|
||||
|
||||
=item C<languages>
|
||||
|
||||
Currently installed languages.
|
||||
Returns a reference to a list of RFC 1766 language tags of installed languages.
|
||||
|
||||
=item C<switch_to_shadow_db>
|
||||
|
||||
Switch from using the main database to using the shadow database.
|
||||
|
||||
=item C<switch_to_main_db>
|
||||
|
||||
Change the database object to refer to the main database.
|
||||
|
||||
=item C<params>
|
||||
|
||||
The current Parameters of Bugzilla, as a hashref. If C<data/params>
|
||||
does not exist, then we return an empty hashref. If C<data/params>
|
||||
is unreadable or is not valid perl, we C<die>.
|
||||
|
||||
=item C<local_timezone>
|
||||
|
||||
Returns the local timezone of the Bugzilla installation,
|
||||
as a DateTime::TimeZone object. This detection is very time
|
||||
consuming, so we cache this information for future references.
|
||||
|
||||
=item C<job_queue>
|
||||
|
||||
Returns a L<Bugzilla::JobQueue> that you can use for queueing jobs.
|
||||
Will throw an error if job queueing is not correctly configured on
|
||||
this Bugzilla installation.
|
||||
|
||||
=item C<feature>
|
||||
|
||||
Tells you whether or not a specific feature is enabled. For names
|
||||
of features, see C<OPTIONAL_MODULES> in C<Bugzilla::Install::Requirements>.
|
||||
|
||||
=back
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,297 +0,0 @@
|
||||
# -*- 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): John Keiser <john@johnkeiser.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::Attachment::PatchReader;
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Attachment;
|
||||
use Bugzilla::Util;
|
||||
|
||||
sub process_diff {
|
||||
my ($attachment, $format, $context) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $lc = Bugzilla->localconfig;
|
||||
my $vars = {};
|
||||
|
||||
my ($reader, $last_reader) = setup_patch_readers(undef, $context);
|
||||
|
||||
if ($format eq 'raw') {
|
||||
require PatchReader::DiffPrinter::raw;
|
||||
$last_reader->sends_data_to(new PatchReader::DiffPrinter::raw());
|
||||
# Actually print out the patch.
|
||||
print $cgi->header(-type => 'text/plain',
|
||||
-x_content_type_options => "nosniff",
|
||||
-expires => '+3M');
|
||||
disable_utf8();
|
||||
$reader->iterate_string('Attachment ' . $attachment->id, $attachment->data);
|
||||
}
|
||||
else {
|
||||
my @other_patches = ();
|
||||
if ($lc->{interdiffbin} && $lc->{diffpath}) {
|
||||
# Get the list of attachments that the user can view in this bug.
|
||||
my @attachments =
|
||||
@{Bugzilla::Attachment->get_attachments_by_bug($attachment->bug_id)};
|
||||
# Extract patches only.
|
||||
@attachments = grep {$_->ispatch == 1} @attachments;
|
||||
# We want them sorted from newer to older.
|
||||
@attachments = sort { $b->id <=> $a->id } @attachments;
|
||||
|
||||
# Ignore the current patch, but select the one right before it
|
||||
# chronologically.
|
||||
my $select_next_patch = 0;
|
||||
foreach my $attach (@attachments) {
|
||||
if ($attach->id == $attachment->id) {
|
||||
$select_next_patch = 1;
|
||||
}
|
||||
else {
|
||||
push(@other_patches, { 'id' => $attach->id,
|
||||
'desc' => $attach->description,
|
||||
'selected' => $select_next_patch });
|
||||
$select_next_patch = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$vars->{'bugid'} = $attachment->bug_id;
|
||||
$vars->{'attachid'} = $attachment->id;
|
||||
$vars->{'description'} = $attachment->description;
|
||||
$vars->{'other_patches'} = \@other_patches;
|
||||
|
||||
setup_template_patch_reader($last_reader, $format, $context, $vars);
|
||||
# The patch is going to be displayed in a HTML page and if the utf8
|
||||
# param is enabled, we have to encode attachment data as utf8.
|
||||
if (Bugzilla->params->{'utf8'}) {
|
||||
$attachment->data; # Populate ->{data}
|
||||
utf8::decode($attachment->{data});
|
||||
}
|
||||
$reader->iterate_string('Attachment ' . $attachment->id, $attachment->data);
|
||||
}
|
||||
}
|
||||
|
||||
sub process_interdiff {
|
||||
my ($old_attachment, $new_attachment, $format, $context) = @_;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $lc = Bugzilla->localconfig;
|
||||
my $vars = {};
|
||||
|
||||
# Encode attachment data as utf8 if it's going to be displayed in a HTML
|
||||
# page using the UTF-8 encoding.
|
||||
if ($format ne 'raw' && Bugzilla->params->{'utf8'}) {
|
||||
$old_attachment->data; # Populate ->{data}
|
||||
utf8::decode($old_attachment->{data});
|
||||
$new_attachment->data; # Populate ->{data}
|
||||
utf8::decode($new_attachment->{data});
|
||||
}
|
||||
|
||||
# Get old patch data.
|
||||
my ($old_filename, $old_file_list) = get_unified_diff($old_attachment, $format);
|
||||
# Get new patch data.
|
||||
my ($new_filename, $new_file_list) = get_unified_diff($new_attachment, $format);
|
||||
|
||||
my $warning = warn_if_interdiff_might_fail($old_file_list, $new_file_list);
|
||||
|
||||
# Send through interdiff, send output directly to template.
|
||||
# Must hack path so that interdiff will work.
|
||||
$ENV{'PATH'} = $lc->{diffpath};
|
||||
open my $interdiff_fh, '-|', "$lc->{interdiffbin} $old_filename $new_filename";
|
||||
binmode $interdiff_fh;
|
||||
my ($reader, $last_reader) = setup_patch_readers("", $context);
|
||||
|
||||
if ($format eq 'raw') {
|
||||
require PatchReader::DiffPrinter::raw;
|
||||
$last_reader->sends_data_to(new PatchReader::DiffPrinter::raw());
|
||||
# Actually print out the patch.
|
||||
print $cgi->header(-type => 'text/plain',
|
||||
-x_content_type_options => "nosniff",
|
||||
-expires => '+3M');
|
||||
disable_utf8();
|
||||
}
|
||||
else {
|
||||
# In case the HTML page is displayed with the UTF-8 encoding.
|
||||
binmode $interdiff_fh, ':utf8' if Bugzilla->params->{'utf8'};
|
||||
|
||||
$vars->{'warning'} = $warning if $warning;
|
||||
$vars->{'bugid'} = $new_attachment->bug_id;
|
||||
$vars->{'oldid'} = $old_attachment->id;
|
||||
$vars->{'old_desc'} = $old_attachment->description;
|
||||
$vars->{'newid'} = $new_attachment->id;
|
||||
$vars->{'new_desc'} = $new_attachment->description;
|
||||
|
||||
setup_template_patch_reader($last_reader, $format, $context, $vars);
|
||||
}
|
||||
$reader->iterate_fh($interdiff_fh, 'interdiff #' . $old_attachment->id .
|
||||
' #' . $new_attachment->id);
|
||||
close $interdiff_fh;
|
||||
$ENV{'PATH'} = '';
|
||||
|
||||
# Delete temporary files.
|
||||
unlink($old_filename) or warn "Could not unlink $old_filename: $!";
|
||||
unlink($new_filename) or warn "Could not unlink $new_filename: $!";
|
||||
}
|
||||
|
||||
######################
|
||||
# Internal routines
|
||||
######################
|
||||
|
||||
sub get_unified_diff {
|
||||
my ($attachment, $format) = @_;
|
||||
|
||||
# Bring in the modules we need.
|
||||
require PatchReader::Raw;
|
||||
require PatchReader::FixPatchRoot;
|
||||
require PatchReader::DiffPrinter::raw;
|
||||
require PatchReader::PatchInfoGrabber;
|
||||
require File::Temp;
|
||||
|
||||
$attachment->ispatch
|
||||
|| ThrowCodeError('must_be_patch', { 'attach_id' => $attachment->id });
|
||||
|
||||
# Reads in the patch, converting to unified diff in a temp file.
|
||||
my $reader = new PatchReader::Raw;
|
||||
my $last_reader = $reader;
|
||||
|
||||
# Fixes patch root (makes canonical if possible).
|
||||
if (Bugzilla->params->{'cvsroot'}) {
|
||||
my $fix_patch_root =
|
||||
new PatchReader::FixPatchRoot(Bugzilla->params->{'cvsroot'});
|
||||
$last_reader->sends_data_to($fix_patch_root);
|
||||
$last_reader = $fix_patch_root;
|
||||
}
|
||||
|
||||
# Grabs the patch file info.
|
||||
my $patch_info_grabber = new PatchReader::PatchInfoGrabber();
|
||||
$last_reader->sends_data_to($patch_info_grabber);
|
||||
$last_reader = $patch_info_grabber;
|
||||
|
||||
# Prints out to temporary file.
|
||||
my ($fh, $filename) = File::Temp::tempfile();
|
||||
if ($format ne 'raw' && Bugzilla->params->{'utf8'}) {
|
||||
# The HTML page will be displayed with the UTF-8 encoding.
|
||||
binmode $fh, ':utf8';
|
||||
}
|
||||
my $raw_printer = new PatchReader::DiffPrinter::raw($fh);
|
||||
$last_reader->sends_data_to($raw_printer);
|
||||
$last_reader = $raw_printer;
|
||||
|
||||
# Iterate!
|
||||
$reader->iterate_string($attachment->id, $attachment->data);
|
||||
|
||||
return ($filename, $patch_info_grabber->patch_info()->{files});
|
||||
}
|
||||
|
||||
sub warn_if_interdiff_might_fail {
|
||||
my ($old_file_list, $new_file_list) = @_;
|
||||
|
||||
# Verify that the list of files diffed is the same.
|
||||
my @old_files = sort keys %{$old_file_list};
|
||||
my @new_files = sort keys %{$new_file_list};
|
||||
if (@old_files != @new_files
|
||||
|| join(' ', @old_files) ne join(' ', @new_files))
|
||||
{
|
||||
return 'interdiff1';
|
||||
}
|
||||
|
||||
# Verify that the revisions in the files are the same.
|
||||
foreach my $file (keys %{$old_file_list}) {
|
||||
if ($old_file_list->{$file}{old_revision} ne
|
||||
$new_file_list->{$file}{old_revision})
|
||||
{
|
||||
return 'interdiff2';
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub setup_patch_readers {
|
||||
my ($diff_root, $context) = @_;
|
||||
|
||||
# Parameters:
|
||||
# format=raw|html
|
||||
# context=patch|file|0-n
|
||||
# collapsed=0|1
|
||||
# headers=0|1
|
||||
|
||||
# Define the patch readers.
|
||||
# The reader that reads the patch in (whatever its format).
|
||||
require PatchReader::Raw;
|
||||
my $reader = new PatchReader::Raw;
|
||||
my $last_reader = $reader;
|
||||
# Fix the patch root if we have a cvs root.
|
||||
if (Bugzilla->params->{'cvsroot'}) {
|
||||
require PatchReader::FixPatchRoot;
|
||||
$last_reader->sends_data_to(new PatchReader::FixPatchRoot(Bugzilla->params->{'cvsroot'}));
|
||||
$last_reader->sends_data_to->diff_root($diff_root) if defined($diff_root);
|
||||
$last_reader = $last_reader->sends_data_to;
|
||||
}
|
||||
|
||||
# Add in cvs context if we have the necessary info to do it
|
||||
if ($context ne 'patch' && Bugzilla->localconfig->{cvsbin}
|
||||
&& Bugzilla->params->{'cvsroot_get'})
|
||||
{
|
||||
require PatchReader::AddCVSContext;
|
||||
# We need to set $cvsbin as global, because PatchReader::CVSClient
|
||||
# needs it in order to find 'cvs'.
|
||||
$main::cvsbin = Bugzilla->localconfig->{cvsbin};
|
||||
$last_reader->sends_data_to(
|
||||
new PatchReader::AddCVSContext($context, Bugzilla->params->{'cvsroot_get'}));
|
||||
$last_reader = $last_reader->sends_data_to;
|
||||
}
|
||||
|
||||
return ($reader, $last_reader);
|
||||
}
|
||||
|
||||
sub setup_template_patch_reader {
|
||||
my ($last_reader, $format, $context, $vars) = @_;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $template = Bugzilla->template;
|
||||
|
||||
require PatchReader::DiffPrinter::template;
|
||||
|
||||
# Define the vars for templates.
|
||||
if (defined $cgi->param('headers')) {
|
||||
$vars->{'headers'} = $cgi->param('headers');
|
||||
}
|
||||
else {
|
||||
$vars->{'headers'} = 1;
|
||||
}
|
||||
|
||||
$vars->{'collapsed'} = $cgi->param('collapsed');
|
||||
$vars->{'context'} = $context;
|
||||
$vars->{'do_context'} = Bugzilla->localconfig->{cvsbin}
|
||||
&& Bugzilla->params->{'cvsroot_get'} && !$vars->{'newid'};
|
||||
|
||||
# Print everything out.
|
||||
print $cgi->header(-type => 'text/html',
|
||||
-expires => '+3M');
|
||||
|
||||
$last_reader->sends_data_to(new PatchReader::DiffPrinter::template($template,
|
||||
"attachment/diff-header.$format.tmpl",
|
||||
"attachment/diff-file.$format.tmpl",
|
||||
"attachment/diff-footer.$format.tmpl",
|
||||
{ %{$vars},
|
||||
bonsai_url => Bugzilla->params->{'bonsai_url'},
|
||||
lxr_url => Bugzilla->params->{'lxr_url'},
|
||||
lxr_root => Bugzilla->params->{'lxr_root'},
|
||||
}));
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
@@ -1,525 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth;
|
||||
|
||||
use strict;
|
||||
use fields qw(
|
||||
_info_getter
|
||||
_verifier
|
||||
_persister
|
||||
);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Mailer;
|
||||
use Bugzilla::Util qw(datetime_from);
|
||||
use Bugzilla::User::Setting ();
|
||||
use Bugzilla::Auth::Login::Stack;
|
||||
use Bugzilla::Auth::Verify::Stack;
|
||||
use Bugzilla::Auth::Persist::Cookie;
|
||||
|
||||
sub new {
|
||||
my ($class, $params) = @_;
|
||||
my $self = fields::new($class);
|
||||
|
||||
$params ||= {};
|
||||
$params->{Login} ||= Bugzilla->params->{'user_info_class'} . ',Cookie';
|
||||
$params->{Verify} ||= Bugzilla->params->{'user_verify_class'};
|
||||
|
||||
$self->{_info_getter} = new Bugzilla::Auth::Login::Stack($params->{Login});
|
||||
$self->{_verifier} = new Bugzilla::Auth::Verify::Stack($params->{Verify});
|
||||
# If we ever have any other login persistence methods besides cookies,
|
||||
# this could become more configurable.
|
||||
$self->{_persister} = new Bugzilla::Auth::Persist::Cookie();
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub login {
|
||||
my ($self, $type) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# Get login info from the cookie, form, environment variables, etc.
|
||||
my $login_info = $self->{_info_getter}->get_login_info();
|
||||
|
||||
if ($login_info->{failure}) {
|
||||
return $self->_handle_login_result($login_info, $type);
|
||||
}
|
||||
|
||||
# Now verify his username and password against the DB, LDAP, etc.
|
||||
if ($self->{_info_getter}->{successful}->requires_verification) {
|
||||
$login_info = $self->{_verifier}->check_credentials($login_info);
|
||||
if ($login_info->{failure}) {
|
||||
return $self->_handle_login_result($login_info, $type);
|
||||
}
|
||||
$login_info =
|
||||
$self->{_verifier}->{successful}->create_or_update_user($login_info);
|
||||
}
|
||||
else {
|
||||
$login_info = $self->{_verifier}->create_or_update_user($login_info);
|
||||
}
|
||||
|
||||
if ($login_info->{failure}) {
|
||||
return $self->_handle_login_result($login_info, $type);
|
||||
}
|
||||
|
||||
# Make sure the user isn't disabled.
|
||||
my $user = $login_info->{user};
|
||||
if ($user->disabledtext) {
|
||||
return $self->_handle_login_result({ failure => AUTH_DISABLED,
|
||||
user => $user }, $type);
|
||||
}
|
||||
$user->set_authorizer($self);
|
||||
|
||||
return $self->_handle_login_result($login_info, $type);
|
||||
}
|
||||
|
||||
sub can_change_password {
|
||||
my ($self) = @_;
|
||||
my $verifier = $self->{_verifier}->{successful};
|
||||
$verifier ||= $self->{_verifier};
|
||||
my $getter = $self->{_info_getter}->{successful};
|
||||
$getter = $self->{_info_getter}
|
||||
if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
|
||||
return $verifier->can_change_password &&
|
||||
$getter->user_can_create_account;
|
||||
}
|
||||
|
||||
sub can_login {
|
||||
my ($self) = @_;
|
||||
my $getter = $self->{_info_getter}->{successful};
|
||||
$getter = $self->{_info_getter}
|
||||
if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
|
||||
return $getter->can_login;
|
||||
}
|
||||
|
||||
sub can_logout {
|
||||
my ($self) = @_;
|
||||
my $getter = $self->{_info_getter}->{successful};
|
||||
# If there's no successful getter, we're not logged in, so of
|
||||
# course we can't log out!
|
||||
return 0 unless $getter;
|
||||
return $getter->can_logout;
|
||||
}
|
||||
|
||||
sub user_can_create_account {
|
||||
my ($self) = @_;
|
||||
my $verifier = $self->{_verifier}->{successful};
|
||||
$verifier ||= $self->{_verifier};
|
||||
my $getter = $self->{_info_getter}->{successful};
|
||||
$getter = $self->{_info_getter}
|
||||
if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
|
||||
return $verifier->user_can_create_account
|
||||
&& $getter->user_can_create_account;
|
||||
}
|
||||
|
||||
sub can_change_email {
|
||||
return $_[0]->user_can_create_account;
|
||||
}
|
||||
|
||||
sub _handle_login_result {
|
||||
my ($self, $result, $login_type) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $user = $result->{user};
|
||||
my $fail_code = $result->{failure};
|
||||
|
||||
if (!$fail_code) {
|
||||
# We don't persist logins over GET requests in the WebService,
|
||||
# because the persistance information can't be re-used again.
|
||||
# (See Bugzilla::WebService::Server::JSONRPC for more info.)
|
||||
if ($self->{_info_getter}->{successful}->requires_persistence
|
||||
and !Bugzilla->request_cache->{auth_no_automatic_login})
|
||||
{
|
||||
$self->{_persister}->persist_login($user);
|
||||
}
|
||||
}
|
||||
elsif ($fail_code == AUTH_ERROR) {
|
||||
if ($result->{user_error}) {
|
||||
ThrowUserError($result->{user_error}, $result->{details});
|
||||
}
|
||||
else {
|
||||
ThrowCodeError($result->{error}, $result->{details});
|
||||
}
|
||||
}
|
||||
elsif ($fail_code == AUTH_NODATA) {
|
||||
$self->{_info_getter}->fail_nodata($self)
|
||||
if $login_type == LOGIN_REQUIRED;
|
||||
|
||||
# If we're not LOGIN_REQUIRED, we just return the default user.
|
||||
$user = Bugzilla->user;
|
||||
}
|
||||
# The username/password may be wrong
|
||||
# Don't let the user know whether the username exists or whether
|
||||
# the password was just wrong. (This makes it harder for a cracker
|
||||
# to find account names by brute force)
|
||||
elsif ($fail_code == AUTH_LOGINFAILED or $fail_code == AUTH_NO_SUCH_USER) {
|
||||
my $remaining_attempts = MAX_LOGIN_ATTEMPTS
|
||||
- ($result->{failure_count} || 0);
|
||||
ThrowUserError("invalid_username_or_password",
|
||||
{ remaining => $remaining_attempts });
|
||||
}
|
||||
# The account may be disabled
|
||||
elsif ($fail_code == AUTH_DISABLED) {
|
||||
$self->{_persister}->logout();
|
||||
# XXX This is NOT a good way to do this, architecturally.
|
||||
$self->{_persister}->clear_browser_cookies();
|
||||
# and throw a user error
|
||||
ThrowUserError("account_disabled",
|
||||
{'disabled_reason' => $result->{user}->disabledtext});
|
||||
}
|
||||
elsif ($fail_code == AUTH_LOCKOUT) {
|
||||
my $attempts = $user->account_ip_login_failures;
|
||||
|
||||
# We want to know when the account will be unlocked. This is
|
||||
# determined by the 5th-from-last login failure (or more/less than
|
||||
# 5th, if MAX_LOGIN_ATTEMPTS is not 5).
|
||||
my $determiner = $attempts->[scalar(@$attempts) - MAX_LOGIN_ATTEMPTS];
|
||||
my $unlock_at = datetime_from($determiner->{login_time},
|
||||
Bugzilla->local_timezone);
|
||||
$unlock_at->add(minutes => LOGIN_LOCKOUT_INTERVAL);
|
||||
|
||||
# If we were *just* locked out, notify the maintainer about the
|
||||
# lockout.
|
||||
if ($result->{just_locked_out}) {
|
||||
# We're sending to the maintainer, who may be not a Bugzilla
|
||||
# account, but just an email address. So we use the
|
||||
# installation's default language for sending the email.
|
||||
my $default_settings = Bugzilla::User::Setting::get_defaults();
|
||||
my $template = Bugzilla->template_inner(
|
||||
$default_settings->{lang}->{default_value});
|
||||
my $vars = {
|
||||
locked_user => $user,
|
||||
attempts => $attempts,
|
||||
unlock_at => $unlock_at,
|
||||
};
|
||||
my $message;
|
||||
$template->process('email/lockout.txt.tmpl', $vars, \$message)
|
||||
|| ThrowTemplateError($template->error);
|
||||
MessageToMTA($message);
|
||||
}
|
||||
|
||||
$unlock_at->set_time_zone($user->timezone);
|
||||
ThrowUserError('account_locked',
|
||||
{ ip_addr => $determiner->{ip_addr}, unlock_at => $unlock_at });
|
||||
}
|
||||
# If we get here, then we've run out of options, which shouldn't happen.
|
||||
else {
|
||||
ThrowCodeError("authres_unhandled", { value => $fail_code });
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth - An object that authenticates the login credentials for
|
||||
a user.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Handles authentication for Bugzilla users.
|
||||
|
||||
Authentication from Bugzilla involves two sets of modules. One set is
|
||||
used to obtain the username/password (from CGI, email, etc), and the
|
||||
other set uses this data to authenticate against the datasource
|
||||
(the Bugzilla DB, LDAP, PAM, etc.).
|
||||
|
||||
Modules for obtaining the username/password are subclasses of
|
||||
L<Bugzilla::Auth::Login>, and modules for authenticating are subclasses
|
||||
of L<Bugzilla::Auth::Verify>.
|
||||
|
||||
=head1 AUTHENTICATION ERROR CODES
|
||||
|
||||
Whenever a method in the C<Bugzilla::Auth> family fails in some way,
|
||||
it will return a hashref containing at least a single key called C<failure>.
|
||||
C<failure> will point to an integer error code, and depending on the error
|
||||
code the hashref may contain more data.
|
||||
|
||||
The error codes are explained here below.
|
||||
|
||||
=head2 C<AUTH_NODATA>
|
||||
|
||||
Insufficient login data was provided by the user. This may happen in several
|
||||
cases, such as cookie authentication when the cookie is not present.
|
||||
|
||||
=head2 C<AUTH_ERROR>
|
||||
|
||||
An error occurred when trying to use the login mechanism.
|
||||
|
||||
The hashref will also contain an C<error> element, which is the name
|
||||
of an error from C<template/en/default/global/code-error.html> --
|
||||
the same type of error that would be thrown by
|
||||
L<Bugzilla::Error::ThrowCodeError>.
|
||||
|
||||
The hashref *may* contain an element called C<details>, which is a hashref
|
||||
that should be passed to L<Bugzilla::Error::ThrowCodeError> as the
|
||||
various fields to be used in the error message.
|
||||
|
||||
=head2 C<AUTH_LOGINFAILED>
|
||||
|
||||
An incorrect username or password was given.
|
||||
|
||||
The hashref may also contain a C<failure_count> element, which specifies
|
||||
how many times the account has failed to log in within the lockout
|
||||
period (see L</AUTH_LOCKOUT>). This is used to warn the user when
|
||||
he is getting close to being locked out.
|
||||
|
||||
=head2 C<AUTH_NO_SUCH_USER>
|
||||
|
||||
This is an optional more-specific version of C<AUTH_LOGINFAILED>.
|
||||
Modules should throw this error when they discover that the
|
||||
requested user account actually does not exist, according to them.
|
||||
|
||||
That is, for example, L<Bugzilla::Auth::Verify::LDAP> would throw
|
||||
this if the user didn't exist in LDAP.
|
||||
|
||||
The difference between C<AUTH_NO_SUCH_USER> and C<AUTH_LOGINFAILED>
|
||||
should never be communicated to the user, for security reasons.
|
||||
|
||||
=head2 C<AUTH_DISABLED>
|
||||
|
||||
The user successfully logged in, but their account has been disabled.
|
||||
Usually this is throw only by C<Bugzilla::Auth::login>.
|
||||
|
||||
=head2 C<AUTH_LOCKOUT>
|
||||
|
||||
The user's account is locked out after having failed to log in too many
|
||||
times within a certain period of time (as specified by
|
||||
L<Bugzilla::Constants/LOGIN_LOCKOUT_INTERVAL>).
|
||||
|
||||
The hashref will also contain a C<user> element, representing the
|
||||
L<Bugzilla::User> whose account is locked out.
|
||||
|
||||
=head1 LOGIN TYPES
|
||||
|
||||
The C<login> function (below) can do different types of login, depending
|
||||
on what constant you pass into it:
|
||||
|
||||
=head2 C<LOGIN_OPTIONAL>
|
||||
|
||||
A login is never required to access this data. Attempting to login is
|
||||
still useful, because this allows the page to be personalised. Note that
|
||||
an incorrect login will still trigger an error, even though the lack of
|
||||
a login will be OK.
|
||||
|
||||
=head2 C<LOGIN_NORMAL>
|
||||
|
||||
A login may or may not be required, depending on the setting of the
|
||||
I<requirelogin> parameter. This is the default if you don't specify a
|
||||
type.
|
||||
|
||||
=head2 C<LOGIN_REQUIRED>
|
||||
|
||||
A login is always required to access this data.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
These are methods that can be called on a C<Bugzilla::Auth> object
|
||||
itself.
|
||||
|
||||
=head2 Login
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<login($type)>
|
||||
|
||||
Description: Logs a user in. For more details on how this works
|
||||
internally, see the section entitled "STRUCTURE."
|
||||
Params: $type - One of the Login Types from above.
|
||||
Returns: An authenticated C<Bugzilla::User>. Or, if the type was
|
||||
not C<LOGIN_REQUIRED>, then we return an
|
||||
empty C<Bugzilla::User> if no login data was passed in.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Info Methods
|
||||
|
||||
These are methods that give information about the Bugzilla::Auth object.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<can_change_password>
|
||||
|
||||
Description: Tells you whether or not the current login system allows
|
||||
changing passwords.
|
||||
Params: None
|
||||
Returns: C<true> if users and administrators should be allowed to
|
||||
change passwords, C<false> otherwise.
|
||||
|
||||
=item C<can_login>
|
||||
|
||||
Description: Tells you whether or not the current login system allows
|
||||
users to log in through the web interface.
|
||||
Params: None
|
||||
Returns: C<true> if users can log in through the web interface,
|
||||
C<false> otherwise.
|
||||
|
||||
=item C<can_logout>
|
||||
|
||||
Description: Tells you whether or not the current login system allows
|
||||
users to log themselves out.
|
||||
Params: None
|
||||
Returns: C<true> if users can log themselves out, C<false> otherwise.
|
||||
If a user isn't logged in, we always return C<false>.
|
||||
|
||||
=item C<user_can_create_account>
|
||||
|
||||
Description: Tells you whether or not users are allowed to manually create
|
||||
their own accounts, based on the current login system in use.
|
||||
Note that this doesn't check the C<createemailregexp>
|
||||
parameter--you have to do that by yourself in your code.
|
||||
Params: None
|
||||
Returns: C<true> if users are allowed to create new Bugzilla accounts,
|
||||
C<false> otherwise.
|
||||
|
||||
=item C<can_change_email>
|
||||
|
||||
Description: Whether or not the current login system allows users to
|
||||
change their own email address.
|
||||
Params: None
|
||||
Returns: C<true> if users can change their own email address,
|
||||
C<false> otherwise.
|
||||
|
||||
=back
|
||||
|
||||
=head1 STRUCTURE
|
||||
|
||||
This section is mostly interesting to developers who want to implement
|
||||
a new authentication type. It describes the general structure of the
|
||||
Bugzilla::Auth family, and how the C<login> function works.
|
||||
|
||||
A C<Bugzilla::Auth> object is essentially a collection of a few other
|
||||
objects: the "Info Getter," the "Verifier," and the "Persistence
|
||||
Mechanism."
|
||||
|
||||
They are used inside the C<login> function in the following order:
|
||||
|
||||
=head2 The Info Getter
|
||||
|
||||
This is a C<Bugzilla::Auth::Login> object. Basically, it gets the
|
||||
username and password from the user, somehow. Or, it just gets enough
|
||||
information to uniquely identify a user, and passes that on down the line.
|
||||
(For example, a C<user_id> is enough to uniquely identify a user,
|
||||
even without a username and password.)
|
||||
|
||||
Some Info Getters don't require any verification. For example, if we got
|
||||
the C<user_id> from a Cookie, we don't need to check the username and
|
||||
password.
|
||||
|
||||
If an Info Getter returns only a C<user_id> and no username/password,
|
||||
then it MUST NOT require verification. If an Info Getter requires
|
||||
verfication, then it MUST return at least a C<username>.
|
||||
|
||||
=head2 The Verifier
|
||||
|
||||
This verifies that the username and password are valid.
|
||||
|
||||
It's possible that some methods of verification don't require a password.
|
||||
|
||||
=head2 The Persistence Mechanism
|
||||
|
||||
This makes it so that the user doesn't have to log in on every page.
|
||||
Normally this object just sends a cookie to the user's web browser,
|
||||
as that's the most common method of "login persistence."
|
||||
|
||||
=head2 Other Things We Do
|
||||
|
||||
After we verify the username and password, sometimes we automatically
|
||||
create an account in the Bugzilla database, for certain authentication
|
||||
types. We use the "Account Source" to get data about the user, and
|
||||
create them in the database. (Or, if their data has changed since the
|
||||
last time they logged in, their data gets updated.)
|
||||
|
||||
=head2 The C<$login_data> Hash
|
||||
|
||||
All of the C<Bugzilla::Auth::Login> and C<Bugzilla::Auth::Verify>
|
||||
methods take an argument called C<$login_data>. This is basically
|
||||
a hash that becomes more and more populated as we go through the
|
||||
C<login> function.
|
||||
|
||||
All C<Bugzilla::Auth::Login> and C<Bugzilla::Auth::Verify> methods
|
||||
also *return* the C<$login_data> structure, when they succeed. They
|
||||
may have added new data to it.
|
||||
|
||||
For all C<Bugzilla::Auth::Login> and C<Bugzilla::Auth::Verify> methods,
|
||||
the rule is "you must return the same hashref you were passed in." You can
|
||||
modify the hashref all you want, but you can't create a new one. The only
|
||||
time you can return a new one is if you're returning some error code
|
||||
instead of the C<$login_data> structure.
|
||||
|
||||
Each C<Bugzilla::Auth::Login> or C<Bugzilla::Auth::Verify> method
|
||||
explains in its documentation which C<$login_data> elements are
|
||||
required by it, and which are set by it.
|
||||
|
||||
Here are all of the elements that *may* be in C<$login_data>:
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<user_id>
|
||||
|
||||
A Bugzilla C<user_id> that uniquely identifies a user.
|
||||
|
||||
=item C<username>
|
||||
|
||||
The username that was provided by the user.
|
||||
|
||||
=item C<bz_username>
|
||||
|
||||
The username of this user inside of Bugzilla. Sometimes this differs from
|
||||
C<username>.
|
||||
|
||||
=item C<password>
|
||||
|
||||
The password provided by the user.
|
||||
|
||||
=item C<realname>
|
||||
|
||||
The real name of the user.
|
||||
|
||||
=item C<extern_id>
|
||||
|
||||
Some string that uniquely identifies the user in an external account
|
||||
source. If this C<extern_id> already exists in the database with
|
||||
a different username, the username will be *changed* to be the
|
||||
username specified in this C<$login_data>.
|
||||
|
||||
That is, let's my extern_id is C<mkanat>. I already have an account
|
||||
in Bugzilla with the username of C<mkanat@foo.com>. But this time,
|
||||
when I log in, I have an extern_id of C<mkanat> and a C<username>
|
||||
of C<mkanat@bar.org>. So now, Bugzilla will automatically change my
|
||||
username to C<mkanat@bar.org> instead of C<mkanat@foo.com>.
|
||||
|
||||
=item C<user>
|
||||
|
||||
A L<Bugzilla::User> object representing the authenticated user.
|
||||
Note that C<Bugzilla::Auth::login> may modify this object at various points.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
# -*- 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): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login;
|
||||
|
||||
use strict;
|
||||
use fields qw();
|
||||
|
||||
# Determines whether or not a user can logout. It's really a subroutine,
|
||||
# but we implement it here as a constant. Override it in subclasses if
|
||||
# that particular type of login method cannot log out.
|
||||
use constant can_logout => 1;
|
||||
use constant can_login => 1;
|
||||
use constant requires_persistence => 1;
|
||||
use constant requires_verification => 1;
|
||||
use constant user_can_create_account => 0;
|
||||
use constant is_automatic => 0;
|
||||
|
||||
sub new {
|
||||
my ($class) = @_;
|
||||
my $self = fields::new($class);
|
||||
return $self;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Login - Gets username/password data from the user.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Bugzilla::Auth::Login is used to get information that uniquely identifies
|
||||
a user and allows us to authorize their Bugzilla access.
|
||||
|
||||
It is mostly an abstract class, requiring subclasses to implement
|
||||
most methods.
|
||||
|
||||
Note that callers outside of the C<Bugzilla::Auth> package should never
|
||||
create this object directly. Just create a C<Bugzilla::Auth> object
|
||||
and call C<login> on it.
|
||||
|
||||
=head1 LOGIN METHODS
|
||||
|
||||
These are methods that have to do with getting the actual login data
|
||||
from the user or handling a login somehow.
|
||||
|
||||
These methods are abstract -- they MUST be implemented by a subclass.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<get_login_info()>
|
||||
|
||||
Description: Gets a username/password from the user, or some other
|
||||
information that uniquely identifies them.
|
||||
Params: None
|
||||
Returns: A C<$login_data> hashref. (See L<Bugzilla::Auth> for details.)
|
||||
The hashref MUST contain: C<user_id> *or* C<username>
|
||||
If this is a login method that requires verification,
|
||||
the hashref MUST contain C<password>.
|
||||
The hashref MAY contain C<realname> and C<extern_id>.
|
||||
|
||||
=item C<fail_nodata()>
|
||||
|
||||
Description: This function is called when Bugzilla doesn't get
|
||||
a username/password and the login type is C<LOGIN_REQUIRED>
|
||||
(See L<Bugzilla::Auth> for a description of C<LOGIN_REQUIRED>).
|
||||
That is, this handles C<AUTH_NODATA> in that situation.
|
||||
|
||||
This function MUST stop CGI execution when it is complete.
|
||||
That is, it must call C<exit> or C<ThrowUserError> or some
|
||||
such thing.
|
||||
Params: None
|
||||
Returns: Never Returns.
|
||||
|
||||
=back
|
||||
|
||||
=head1 INFO METHODS
|
||||
|
||||
These are methods that describe the capabilities of this
|
||||
C<Bugzilla::Auth::Login> object. These are all no-parameter
|
||||
methods that return either C<true> or C<false>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<can_logout>
|
||||
|
||||
Whether or not users can log out if they logged in using this
|
||||
object. Defaults to C<true>.
|
||||
|
||||
=item C<can_login>
|
||||
|
||||
Whether or not users can log in through the web interface using
|
||||
this object. Defaults to C<true>.
|
||||
|
||||
=item C<requires_persistence>
|
||||
|
||||
Whether or not we should send the user a cookie if they logged in with
|
||||
this method. Defaults to C<true>.
|
||||
|
||||
=item C<requires_verification>
|
||||
|
||||
Whether or not we should check the username/password that we
|
||||
got from this login method. Defaults to C<true>.
|
||||
|
||||
=item C<user_can_create_account>
|
||||
|
||||
Whether or not users can create accounts, if this login method is
|
||||
currently being used by the system. Defaults to C<false>.
|
||||
|
||||
=item C<is_automatic>
|
||||
|
||||
True if this login method requires no interaction from the user within
|
||||
Bugzilla. (For example, C<Env> auth is "automatic" because the webserver
|
||||
just passes us an environment variable on most page requests, and does not
|
||||
ask the user for authentication information directly in Bugzilla.) Defaults
|
||||
to C<false>.
|
||||
|
||||
=back
|
||||
@@ -1,71 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login::CGI;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Login);
|
||||
use constant user_can_create_account => 1;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
|
||||
sub get_login_info {
|
||||
my ($self) = @_;
|
||||
my $params = Bugzilla->input_params;
|
||||
|
||||
my $username = trim(delete $params->{"Bugzilla_login"});
|
||||
my $password = delete $params->{"Bugzilla_password"};
|
||||
|
||||
if (!defined $username || !defined $password) {
|
||||
return { failure => AUTH_NODATA };
|
||||
}
|
||||
|
||||
return { username => $username, password => $password };
|
||||
}
|
||||
|
||||
sub fail_nodata {
|
||||
my ($self) = @_;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $template = Bugzilla->template;
|
||||
|
||||
if (Bugzilla->usage_mode != USAGE_MODE_BROWSER) {
|
||||
ThrowUserError('login_required');
|
||||
}
|
||||
|
||||
print $cgi->header();
|
||||
$template->process("account/auth/login.html.tmpl",
|
||||
{ 'target' => $cgi->url(-relative=>1) })
|
||||
|| ThrowTemplateError($template->error());
|
||||
exit;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,88 +0,0 @@
|
||||
# -*- 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): Bradley Baetz <bbaetz@acm.org>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login::Cookie;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Login);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
|
||||
use List::Util qw(first);
|
||||
|
||||
use constant requires_persistence => 0;
|
||||
use constant requires_verification => 0;
|
||||
use constant can_login => 0;
|
||||
use constant is_automatic => 1;
|
||||
|
||||
# Note that Cookie never consults the Verifier, it always assumes
|
||||
# it has a valid DB account or it fails.
|
||||
sub get_login_info {
|
||||
my ($self) = @_;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $ip_addr = remote_ip();
|
||||
my $login_cookie = $cgi->cookie("Bugzilla_logincookie");
|
||||
my $user_id = $cgi->cookie("Bugzilla_login");
|
||||
|
||||
# If cookies cannot be found, this could mean that they haven't
|
||||
# been made available yet. In this case, look at Bugzilla_cookie_list.
|
||||
unless ($login_cookie) {
|
||||
my $cookie = first {$_->name eq 'Bugzilla_logincookie'}
|
||||
@{$cgi->{'Bugzilla_cookie_list'}};
|
||||
$login_cookie = $cookie->value if $cookie;
|
||||
}
|
||||
unless ($user_id) {
|
||||
my $cookie = first {$_->name eq 'Bugzilla_login'}
|
||||
@{$cgi->{'Bugzilla_cookie_list'}};
|
||||
$user_id = $cookie->value if $cookie;
|
||||
}
|
||||
|
||||
if ($login_cookie && $user_id) {
|
||||
# Anything goes for these params - they're just strings which
|
||||
# we're going to verify against the db
|
||||
trick_taint($ip_addr);
|
||||
trick_taint($login_cookie);
|
||||
detaint_natural($user_id);
|
||||
|
||||
my $db_cookie =
|
||||
$dbh->selectrow_array('SELECT cookie
|
||||
FROM logincookies
|
||||
WHERE cookie = ?
|
||||
AND userid = ?
|
||||
AND (ipaddr = ? OR ipaddr IS NULL)',
|
||||
undef, ($login_cookie, $user_id, $ip_addr));
|
||||
|
||||
# If the cookie is valid, return a valid username.
|
||||
if (defined $db_cookie && $login_cookie eq $db_cookie) {
|
||||
# If we logged in successfully, then update the lastused
|
||||
# time on the login cookie
|
||||
$dbh->do("UPDATE logincookies SET lastused = NOW()
|
||||
WHERE cookie = ?", undef, $login_cookie);
|
||||
return { user_id => $user_id };
|
||||
}
|
||||
}
|
||||
|
||||
# Either the he cookie is invalid, or we got no cookie. We don't want
|
||||
# to ever return AUTH_LOGINFAILED, because we don't want Bugzilla to
|
||||
# actually throw an error when it gets a bad cookie. It should just
|
||||
# look like there was no cookie to begin with.
|
||||
return { failure => AUTH_NODATA };
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,54 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Erik Stambaugh <erik@dasbistro.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login::Env;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Login);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
|
||||
use constant can_logout => 0;
|
||||
use constant can_login => 0;
|
||||
use constant requires_persistence => 0;
|
||||
use constant requires_verification => 0;
|
||||
use constant is_automatic => 1;
|
||||
|
||||
sub get_login_info {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $env_id = $ENV{Bugzilla->params->{"auth_env_id"}} || '';
|
||||
my $env_email = $ENV{Bugzilla->params->{"auth_env_email"}} || '';
|
||||
my $env_realname = $ENV{Bugzilla->params->{"auth_env_realname"}} || '';
|
||||
|
||||
return { failure => AUTH_NODATA } if !$env_email;
|
||||
|
||||
return { username => $env_email, extern_id => $env_id,
|
||||
realname => $env_realname };
|
||||
}
|
||||
|
||||
sub fail_nodata {
|
||||
ThrowCodeError('env_no_email');
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,100 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login::Stack;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Login);
|
||||
use fields qw(
|
||||
_stack
|
||||
successful
|
||||
);
|
||||
use Hash::Util qw(lock_keys);
|
||||
use Bugzilla::Hook;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
my $list = shift;
|
||||
my %methods = map { $_ => "Bugzilla/Auth/Login/$_.pm" } split(',', $list);
|
||||
lock_keys(%methods);
|
||||
Bugzilla::Hook::process('auth_login_methods', { modules => \%methods });
|
||||
|
||||
$self->{_stack} = [];
|
||||
foreach my $login_method (split(',', $list)) {
|
||||
my $module = $methods{$login_method};
|
||||
require $module;
|
||||
$module =~ s|/|::|g;
|
||||
$module =~ s/.pm$//;
|
||||
push(@{$self->{_stack}}, $module->new(@_));
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_login_info {
|
||||
my $self = shift;
|
||||
my $result;
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
# See Bugzilla::WebService::Server::JSONRPC for where and why
|
||||
# auth_no_automatic_login is used.
|
||||
if (Bugzilla->request_cache->{auth_no_automatic_login}) {
|
||||
next if $object->is_automatic;
|
||||
}
|
||||
$result = $object->get_login_info(@_);
|
||||
$self->{successful} = $object;
|
||||
last if !$result->{failure};
|
||||
# So that if none of them succeed, it's undef.
|
||||
$self->{successful} = undef;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub fail_nodata {
|
||||
my $self = shift;
|
||||
# We fail from the bottom of the stack.
|
||||
my @reverse_stack = reverse @{$self->{_stack}};
|
||||
foreach my $object (@reverse_stack) {
|
||||
# We pick the first object that actually has the method
|
||||
# implemented.
|
||||
if ($object->can('fail_nodata')) {
|
||||
$object->fail_nodata(@_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub can_login {
|
||||
my ($self) = @_;
|
||||
# We return true if any method can log in.
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
return 1 if $object->can_login;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub user_can_create_account {
|
||||
my ($self) = @_;
|
||||
# We return true if any method allows users to create accounts.
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
return 1 if $object->user_can_create_account;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,163 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Persist::Cookie;
|
||||
use strict;
|
||||
use fields qw();
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Token;
|
||||
|
||||
use List::Util qw(first);
|
||||
|
||||
sub new {
|
||||
my ($class) = @_;
|
||||
my $self = fields::new($class);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub persist_login {
|
||||
my ($self, $user) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $input_params = Bugzilla->input_params;
|
||||
|
||||
my $ip_addr;
|
||||
if ($input_params->{'Bugzilla_restrictlogin'}) {
|
||||
$ip_addr = remote_ip();
|
||||
# The IP address is valid, at least for comparing with itself in a
|
||||
# subsequent login
|
||||
trick_taint($ip_addr);
|
||||
}
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
my $login_cookie =
|
||||
Bugzilla::Token::GenerateUniqueToken('logincookies', 'cookie');
|
||||
|
||||
$dbh->do("INSERT INTO logincookies (cookie, userid, ipaddr, lastused)
|
||||
VALUES (?, ?, ?, NOW())",
|
||||
undef, $login_cookie, $user->id, $ip_addr);
|
||||
|
||||
# Issuing a new cookie is a good time to clean up the old
|
||||
# cookies.
|
||||
$dbh->do("DELETE FROM logincookies WHERE lastused < LOCALTIMESTAMP(0) - "
|
||||
. $dbh->sql_interval(MAX_LOGINCOOKIE_AGE, 'DAY'));
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
|
||||
# Prevent JavaScript from accessing login cookies.
|
||||
my %cookieargs = ('-httponly' => 1);
|
||||
|
||||
# Remember cookie only if admin has told so
|
||||
# or admin didn't forbid it and user told to remember.
|
||||
if ( Bugzilla->params->{'rememberlogin'} eq 'on' ||
|
||||
(Bugzilla->params->{'rememberlogin'} ne 'off' &&
|
||||
$input_params->{'Bugzilla_remember'} &&
|
||||
$input_params->{'Bugzilla_remember'} eq 'on') )
|
||||
{
|
||||
# Not a session cookie, so set an infinite expiry
|
||||
$cookieargs{'-expires'} = 'Fri, 01-Jan-2038 00:00:00 GMT';
|
||||
}
|
||||
if (Bugzilla->params->{'ssl_redirect'}) {
|
||||
# Make these cookies only be sent to us by the browser during
|
||||
# HTTPS sessions, if we're using SSL.
|
||||
$cookieargs{'-secure'} = 1;
|
||||
}
|
||||
|
||||
$cgi->send_cookie(-name => 'Bugzilla_login',
|
||||
-value => $user->id,
|
||||
%cookieargs);
|
||||
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
|
||||
-value => $login_cookie,
|
||||
%cookieargs);
|
||||
}
|
||||
|
||||
sub logout {
|
||||
my ($self, $param) = @_;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
$param = {} unless $param;
|
||||
my $user = $param->{user} || Bugzilla->user;
|
||||
my $type = $param->{type} || LOGOUT_ALL;
|
||||
|
||||
if ($type == LOGOUT_ALL) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE userid = ?",
|
||||
undef, $user->id);
|
||||
return;
|
||||
}
|
||||
|
||||
# The LOGOUT_*_CURRENT options require the current login cookie.
|
||||
# If a new cookie has been issued during this run, that's the current one.
|
||||
# If not, it's the one we've received.
|
||||
my $cookie = first {$_->name eq 'Bugzilla_logincookie'}
|
||||
@{$cgi->{'Bugzilla_cookie_list'}};
|
||||
my $login_cookie;
|
||||
if ($cookie) {
|
||||
$login_cookie = $cookie->value;
|
||||
}
|
||||
else {
|
||||
$login_cookie = $cgi->cookie("Bugzilla_logincookie");
|
||||
}
|
||||
trick_taint($login_cookie);
|
||||
|
||||
# These queries use both the cookie ID and the user ID as keys. Even
|
||||
# though we know the userid must match, we still check it in the SQL
|
||||
# as a sanity check, since there is no locking here, and if the user
|
||||
# logged out from two machines simultaneously, while someone else
|
||||
# logged in and got the same cookie, we could be logging the other
|
||||
# user out here. Yes, this is very very very unlikely, but why take
|
||||
# chances? - bbaetz
|
||||
if ($type == LOGOUT_KEEP_CURRENT) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE cookie != ? AND userid = ?",
|
||||
undef, $login_cookie, $user->id);
|
||||
} elsif ($type == LOGOUT_CURRENT) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE cookie = ? AND userid = ?",
|
||||
undef, $login_cookie, $user->id);
|
||||
} else {
|
||||
die("Invalid type $type supplied to logout()");
|
||||
}
|
||||
|
||||
if ($type != LOGOUT_KEEP_CURRENT) {
|
||||
clear_browser_cookies();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub clear_browser_cookies {
|
||||
my $cgi = Bugzilla->cgi;
|
||||
$cgi->remove_cookie('Bugzilla_login');
|
||||
$cgi->remove_cookie('Bugzilla_logincookie');
|
||||
$cgi->remove_cookie('sudo');
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,235 +0,0 @@
|
||||
# -*- 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): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Verify;
|
||||
|
||||
use strict;
|
||||
use fields qw();
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
|
||||
use constant user_can_create_account => 1;
|
||||
|
||||
sub new {
|
||||
my ($class, $login_type) = @_;
|
||||
my $self = fields::new($class);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub can_change_password {
|
||||
return $_[0]->can('change_password');
|
||||
}
|
||||
|
||||
sub create_or_update_user {
|
||||
my ($self, $params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $extern_id = $params->{extern_id};
|
||||
my $username = $params->{bz_username} || $params->{username};
|
||||
my $password = $params->{password} || '*';
|
||||
my $real_name = $params->{realname} || '';
|
||||
my $user_id = $params->{user_id};
|
||||
|
||||
# A passed-in user_id always overrides anything else, for determining
|
||||
# what account we should return.
|
||||
if (!$user_id) {
|
||||
my $username_user_id = login_to_id($username || '');
|
||||
my $extern_user_id;
|
||||
if ($extern_id) {
|
||||
trick_taint($extern_id);
|
||||
$extern_user_id = $dbh->selectrow_array('SELECT userid
|
||||
FROM profiles WHERE extern_id = ?', undef, $extern_id);
|
||||
}
|
||||
|
||||
# If we have both a valid extern_id and a valid username, and they are
|
||||
# not the same id, then we have a conflict.
|
||||
if ($username_user_id && $extern_user_id
|
||||
&& $username_user_id ne $extern_user_id)
|
||||
{
|
||||
my $extern_name = Bugzilla::User->new($extern_user_id)->login;
|
||||
return { failure => AUTH_ERROR, error => "extern_id_conflict",
|
||||
details => {extern_id => $extern_id,
|
||||
extern_user => $extern_name,
|
||||
username => $username} };
|
||||
}
|
||||
|
||||
# If we have a valid username, but no valid id,
|
||||
# then we have to create the user. This happens when we're
|
||||
# passed only a username, and that username doesn't exist already.
|
||||
if ($username && !$username_user_id && !$extern_user_id) {
|
||||
validate_email_syntax($username)
|
||||
|| return { failure => AUTH_ERROR,
|
||||
error => 'auth_invalid_email',
|
||||
details => {addr => $username} };
|
||||
# Usually we'd call validate_password, but external authentication
|
||||
# systems might follow different standards than ours. So in this
|
||||
# place here, we call trick_taint without checks.
|
||||
trick_taint($password);
|
||||
|
||||
# XXX Theoretically this could fail with an error, but the fix for
|
||||
# that is too involved to be done right now.
|
||||
my $user = Bugzilla::User->create({
|
||||
login_name => $username,
|
||||
cryptpassword => $password,
|
||||
realname => $real_name});
|
||||
$username_user_id = $user->id;
|
||||
}
|
||||
|
||||
# If we have a valid username id and an extern_id, but no valid
|
||||
# extern_user_id, then we have to set the user's extern_id.
|
||||
if ($extern_id && $username_user_id && !$extern_user_id) {
|
||||
$dbh->do('UPDATE profiles SET extern_id = ? WHERE userid = ?',
|
||||
undef, $extern_id, $username_user_id);
|
||||
}
|
||||
|
||||
# Finally, at this point, one of these will give us a valid user id.
|
||||
$user_id = $extern_user_id || $username_user_id;
|
||||
}
|
||||
|
||||
# If we still don't have a valid user_id, then we weren't passed
|
||||
# enough information in $params, and we should die right here.
|
||||
ThrowCodeError('bad_arg', {argument => 'params', function =>
|
||||
'Bugzilla::Auth::Verify::create_or_update_user'})
|
||||
unless $user_id;
|
||||
|
||||
my $user = new Bugzilla::User($user_id);
|
||||
|
||||
# Now that we have a valid User, we need to see if any data has to be
|
||||
# updated.
|
||||
if ($username && lc($user->login) ne lc($username)) {
|
||||
validate_email_syntax($username)
|
||||
|| return { failure => AUTH_ERROR, error => 'auth_invalid_email',
|
||||
details => {addr => $username} };
|
||||
$user->set_login($username);
|
||||
}
|
||||
if ($real_name && $user->name ne $real_name) {
|
||||
# $real_name is more than likely tainted, but we only use it
|
||||
# in a placeholder and we never use it after this.
|
||||
trick_taint($real_name);
|
||||
$user->set_name($real_name);
|
||||
}
|
||||
$user->update();
|
||||
|
||||
return { user => $user };
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Verify - An object that verifies usernames and passwords.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Bugzilla::Auth::Verify provides the "Verifier" part of the Bugzilla
|
||||
login process. (For details, see the "STRUCTURE" section of
|
||||
L<Bugzilla::Auth>.)
|
||||
|
||||
It is mostly an abstract class, requiring subclasses to implement
|
||||
most methods.
|
||||
|
||||
Note that callers outside of the C<Bugzilla::Auth> package should never
|
||||
create this object directly. Just create a C<Bugzilla::Auth> object
|
||||
and call C<login> on it.
|
||||
|
||||
=head1 VERIFICATION METHODS
|
||||
|
||||
These are the methods that have to do with the actual verification.
|
||||
|
||||
Subclasses MUST implement these methods.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<check_credentials($login_data)>
|
||||
|
||||
Description: Checks whether or not a username is valid.
|
||||
Params: $login_data - A C<$login_data> hashref, as described in
|
||||
L<Bugzilla::Auth>.
|
||||
This C<$login_data> hashref MUST contain
|
||||
C<username>, and SHOULD also contain
|
||||
C<password>.
|
||||
Returns: A C<$login_data> hashref with C<bz_username> set. This
|
||||
method may also set C<realname>. It must avoid changing
|
||||
anything that is already set.
|
||||
|
||||
=back
|
||||
|
||||
=head1 MODIFICATION METHODS
|
||||
|
||||
These are methods that change data in the actual authentication backend.
|
||||
|
||||
These methods are optional, they do not have to be implemented by
|
||||
subclasses.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<create_or_update_user($login_data)>
|
||||
|
||||
Description: Automatically creates a user account in the database
|
||||
if it doesn't already exist, or updates the account
|
||||
data if C<$login_data> contains newer information.
|
||||
|
||||
Params: $login_data - A C<$login_data> hashref, as described in
|
||||
L<Bugzilla::Auth>.
|
||||
This C<$login_data> hashref MUST contain
|
||||
either C<user_id>, C<bz_username>, or
|
||||
C<username>. If both C<username> and C<bz_username>
|
||||
are specified, C<bz_username> is used as the
|
||||
login name of the user to create in the database.
|
||||
It MAY also contain C<extern_id>, in which
|
||||
case it still MUST contain C<bz_username> or
|
||||
C<username>.
|
||||
It MAY contain C<password> and C<realname>.
|
||||
|
||||
Returns: A hashref with one element, C<user>, which is a
|
||||
L<Bugzilla::User> object. May also return a login error
|
||||
as described in L<Bugzilla::Auth>.
|
||||
|
||||
Note: This method is not abstract, it is actually implemented
|
||||
and creates accounts in the Bugzilla database. Subclasses
|
||||
should probably all call the C<Bugzilla::Auth::Verify>
|
||||
version of this function at the end of their own
|
||||
C<create_or_update_user>.
|
||||
|
||||
=item C<change_password($user, $password)>
|
||||
|
||||
Description: Modifies the user's password in the authentication backend.
|
||||
Params: $user - A L<Bugzilla::User> object representing the user
|
||||
whose password we want to change.
|
||||
$password - The user's new password.
|
||||
Returns: Nothing.
|
||||
|
||||
=back
|
||||
|
||||
=head1 INFO METHODS
|
||||
|
||||
These are methods that describe the capabilities of this object.
|
||||
These are all no-parameter methods that return either C<true> or
|
||||
C<false>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<user_can_create_account>
|
||||
|
||||
Whether or not users can manually create accounts in this type of
|
||||
account source. Defaults to C<true>.
|
||||
|
||||
=back
|
||||
@@ -1,108 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Verify::DB;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Verify);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Token;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::User;
|
||||
|
||||
sub check_credentials {
|
||||
my ($self, $login_data) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $username = $login_data->{username};
|
||||
my $user = new Bugzilla::User({ name => $username });
|
||||
|
||||
return { failure => AUTH_NO_SUCH_USER } unless $user;
|
||||
|
||||
$login_data->{user} = $user;
|
||||
$login_data->{bz_username} = $user->login;
|
||||
|
||||
if ($user->account_is_locked_out) {
|
||||
return { failure => AUTH_LOCKOUT, user => $user };
|
||||
}
|
||||
|
||||
my $password = $login_data->{password};
|
||||
my $real_password_crypted = $user->cryptpassword;
|
||||
|
||||
# Using the internal crypted password as the salt,
|
||||
# crypt the password the user entered.
|
||||
my $entered_password_crypted = bz_crypt($password, $real_password_crypted);
|
||||
|
||||
if ($entered_password_crypted ne $real_password_crypted) {
|
||||
# Record the login failure
|
||||
$user->note_login_failure();
|
||||
|
||||
# Immediately check if we are locked out
|
||||
if ($user->account_is_locked_out) {
|
||||
return { failure => AUTH_LOCKOUT, user => $user,
|
||||
just_locked_out => 1 };
|
||||
}
|
||||
|
||||
return { failure => AUTH_LOGINFAILED,
|
||||
failure_count => scalar(@{ $user->account_ip_login_failures }),
|
||||
};
|
||||
}
|
||||
|
||||
# Force the user to type a longer password if it's too short.
|
||||
if (length($password) < USER_PASSWORD_MIN_LENGTH) {
|
||||
return { failure => AUTH_ERROR, user_error => 'password_current_too_short',
|
||||
details => { locked_user => $user } };
|
||||
}
|
||||
|
||||
# The user's credentials are okay, so delete any outstanding
|
||||
# password tokens or login failures they may have generated.
|
||||
Bugzilla::Token::DeletePasswordTokens($user->id, "user_logged_in");
|
||||
$user->clear_login_failures();
|
||||
|
||||
# If their old password was using crypt() or some different hash
|
||||
# than we're using now, convert the stored password to using
|
||||
# whatever hashing system we're using now.
|
||||
my $current_algorithm = PASSWORD_DIGEST_ALGORITHM;
|
||||
if ($real_password_crypted !~ /{\Q$current_algorithm\E}$/) {
|
||||
$user->set_password($password);
|
||||
$user->update();
|
||||
}
|
||||
|
||||
return $login_data;
|
||||
}
|
||||
|
||||
sub change_password {
|
||||
my ($self, $user, $password) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $cryptpassword = bz_crypt($password);
|
||||
$dbh->do("UPDATE profiles SET cryptpassword = ? WHERE userid = ?",
|
||||
undef, $cryptpassword, $user->id);
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,201 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Verify::LDAP;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Verify);
|
||||
use fields qw(
|
||||
ldap
|
||||
);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
|
||||
use Net::LDAP;
|
||||
use Net::LDAP::Util qw(escape_filter_value);
|
||||
|
||||
use constant admin_can_create_account => 0;
|
||||
use constant user_can_create_account => 0;
|
||||
|
||||
sub check_credentials {
|
||||
my ($self, $params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# We need to bind anonymously to the LDAP server. This is
|
||||
# because we need to get the Distinguished Name of the user trying
|
||||
# to log in. Some servers (such as iPlanet) allow you to have unique
|
||||
# uids spread out over a subtree of an area (such as "People"), so
|
||||
# just appending the Base DN to the uid isn't sufficient to get the
|
||||
# user's DN. For servers which don't work this way, there will still
|
||||
# be no harm done.
|
||||
$self->_bind_ldap_for_search();
|
||||
|
||||
# Now, we verify that the user exists, and get a LDAP Distinguished
|
||||
# Name for the user.
|
||||
my $username = $params->{username};
|
||||
my $dn_result = $self->ldap->search(_bz_search_params($username),
|
||||
attrs => ['dn']);
|
||||
return { failure => AUTH_ERROR, error => "ldap_search_error",
|
||||
details => {errstr => $dn_result->error, username => $username}
|
||||
} if $dn_result->code;
|
||||
|
||||
return { failure => AUTH_NO_SUCH_USER } if !$dn_result->count;
|
||||
|
||||
my $dn = $dn_result->shift_entry->dn;
|
||||
|
||||
# Check the password.
|
||||
my $pw_result = $self->ldap->bind($dn, password => $params->{password});
|
||||
return { failure => AUTH_LOGINFAILED } if $pw_result->code;
|
||||
|
||||
# And now we fill in the user's details.
|
||||
|
||||
# First try the search as the (already bound) user in question.
|
||||
my $user_entry;
|
||||
my $error_string;
|
||||
my $detail_result = $self->ldap->search(_bz_search_params($username));
|
||||
if ($detail_result->code) {
|
||||
# Stash away the original error, just in case
|
||||
$error_string = $detail_result->error;
|
||||
} else {
|
||||
$user_entry = $detail_result->shift_entry;
|
||||
}
|
||||
|
||||
# If that failed (either because the search failed, or returned no
|
||||
# results) then try re-binding as the initial search user, but only
|
||||
# if the LDAPbinddn parameter is set.
|
||||
if (!$user_entry && Bugzilla->params->{"LDAPbinddn"}) {
|
||||
$self->_bind_ldap_for_search();
|
||||
|
||||
$detail_result = $self->ldap->search(_bz_search_params($username));
|
||||
if (!$detail_result->code) {
|
||||
$user_entry = $detail_result->shift_entry;
|
||||
}
|
||||
}
|
||||
|
||||
# If we *still* don't have anything in $user_entry then give up.
|
||||
return { failure => AUTH_ERROR, error => "ldap_search_error",
|
||||
details => {errstr => $error_string, username => $username}
|
||||
} if !$user_entry;
|
||||
|
||||
|
||||
my $mail_attr = Bugzilla->params->{"LDAPmailattribute"};
|
||||
if ($mail_attr) {
|
||||
if (!$user_entry->exists($mail_attr)) {
|
||||
return { failure => AUTH_ERROR,
|
||||
error => "ldap_cannot_retreive_attr",
|
||||
details => {attr => $mail_attr} };
|
||||
}
|
||||
|
||||
my @emails = $user_entry->get_value($mail_attr);
|
||||
|
||||
# Default to the first email address returned.
|
||||
$params->{bz_username} = $emails[0];
|
||||
|
||||
if (@emails > 1) {
|
||||
# Cycle through the adresses and check if they're Bugzilla logins.
|
||||
# Use the first one that returns a valid id.
|
||||
foreach my $email (@emails) {
|
||||
if ( login_to_id($email) ) {
|
||||
$params->{bz_username} = $email;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$params->{bz_username} = $username;
|
||||
}
|
||||
|
||||
$params->{realname} ||= $user_entry->get_value("displayName");
|
||||
$params->{realname} ||= $user_entry->get_value("cn");
|
||||
|
||||
$params->{extern_id} = $username;
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
sub _bz_search_params {
|
||||
my ($username) = @_;
|
||||
$username = escape_filter_value($username);
|
||||
return (base => Bugzilla->params->{"LDAPBaseDN"},
|
||||
scope => "sub",
|
||||
filter => '(&(' . Bugzilla->params->{"LDAPuidattribute"}
|
||||
. "=$username)"
|
||||
. Bugzilla->params->{"LDAPfilter"} . ')');
|
||||
}
|
||||
|
||||
sub _bind_ldap_for_search {
|
||||
my ($self) = @_;
|
||||
my $bind_result;
|
||||
if (Bugzilla->params->{"LDAPbinddn"}) {
|
||||
my ($LDAPbinddn,$LDAPbindpass) =
|
||||
split(":",Bugzilla->params->{"LDAPbinddn"});
|
||||
$bind_result =
|
||||
$self->ldap->bind($LDAPbinddn, password => $LDAPbindpass);
|
||||
}
|
||||
else {
|
||||
$bind_result = $self->ldap->bind();
|
||||
}
|
||||
ThrowCodeError("ldap_bind_failed", {errstr => $bind_result->error})
|
||||
if $bind_result->code;
|
||||
}
|
||||
|
||||
# We can't just do this in new(), because we're not allowed to throw any
|
||||
# error from anywhere under Bugzilla::Auth::new -- otherwise we
|
||||
# could create a situation where the admin couldn't get to editparams
|
||||
# to fix his mistake. (Because Bugzilla->login always calls
|
||||
# Bugzilla::Auth->new, and almost every page calls Bugzilla->login.)
|
||||
sub ldap {
|
||||
my ($self) = @_;
|
||||
return $self->{ldap} if $self->{ldap};
|
||||
|
||||
my @servers = split(/[\s,]+/, Bugzilla->params->{"LDAPserver"});
|
||||
ThrowCodeError("ldap_server_not_defined") unless @servers;
|
||||
|
||||
foreach (@servers) {
|
||||
$self->{ldap} = new Net::LDAP(trim($_));
|
||||
last if $self->{ldap};
|
||||
}
|
||||
ThrowCodeError("ldap_connect_failed", { server => join(", ", @servers) })
|
||||
unless $self->{ldap};
|
||||
|
||||
# try to start TLS if needed
|
||||
if (Bugzilla->params->{"LDAPstarttls"}) {
|
||||
my $mesg = $self->{ldap}->start_tls();
|
||||
ThrowCodeError("ldap_start_tls_failed", { error => $mesg->error() })
|
||||
if $mesg->code();
|
||||
}
|
||||
|
||||
return $self->{ldap};
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,64 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Marc Schumann.
|
||||
# Portions created by Marc Schumann are Copyright (c) 2007 Marc Schumann.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
|
||||
|
||||
package Bugzilla::Auth::Verify::RADIUS;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Verify);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
|
||||
use Authen::Radius;
|
||||
|
||||
use constant admin_can_create_account => 0;
|
||||
use constant user_can_create_account => 0;
|
||||
|
||||
sub check_credentials {
|
||||
my ($self, $params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $address_suffix = Bugzilla->params->{'RADIUS_email_suffix'};
|
||||
my $username = $params->{username};
|
||||
|
||||
# If we're using RADIUS_email_suffix, we may need to cut it off from
|
||||
# the login name.
|
||||
if ($address_suffix) {
|
||||
$username =~ s/\Q$address_suffix\E$//i;
|
||||
}
|
||||
|
||||
# Create RADIUS object.
|
||||
my $radius =
|
||||
new Authen::Radius(Host => Bugzilla->params->{'RADIUS_server'},
|
||||
Secret => Bugzilla->params->{'RADIUS_secret'})
|
||||
|| return { failure => AUTH_ERROR, error => 'radius_preparation_error',
|
||||
details => {errstr => Authen::Radius::strerror() } };
|
||||
|
||||
# Check the password.
|
||||
$radius->check_pwd($username, $params->{password},
|
||||
Bugzilla->params->{'RADIUS_NAS_IP'} || undef)
|
||||
|| return { failure => AUTH_LOGINFAILED };
|
||||
|
||||
# Build the user account's e-mail address.
|
||||
$params->{bz_username} = $username . $address_suffix;
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,89 +0,0 @@
|
||||
# -*- 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): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Verify::Stack;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Verify);
|
||||
use fields qw(
|
||||
_stack
|
||||
successful
|
||||
);
|
||||
use Hash::Util qw(lock_keys);
|
||||
use Bugzilla::Hook;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $list = shift;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
my %methods = map { $_ => "Bugzilla/Auth/Verify/$_.pm" } split(',', $list);
|
||||
lock_keys(%methods);
|
||||
Bugzilla::Hook::process('auth_verify_methods', { modules => \%methods });
|
||||
|
||||
$self->{_stack} = [];
|
||||
foreach my $verify_method (split(',', $list)) {
|
||||
my $module = $methods{$verify_method};
|
||||
require $module;
|
||||
$module =~ s|/|::|g;
|
||||
$module =~ s/.pm$//;
|
||||
push(@{$self->{_stack}}, $module->new(@_));
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub can_change_password {
|
||||
my ($self) = @_;
|
||||
# We return true if any method can change passwords.
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
return 1 if $object->can_change_password;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub check_credentials {
|
||||
my $self = shift;
|
||||
my $result;
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
$result = $object->check_credentials(@_);
|
||||
$self->{successful} = $object;
|
||||
last if !$result->{failure};
|
||||
# So that if none of them succeed, it's undef.
|
||||
$self->{successful} = undef;
|
||||
}
|
||||
# Returns the result at the bottom of the stack if they all fail.
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub create_or_update_user {
|
||||
my $self = shift;
|
||||
my $result;
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
$result = $object->create_or_update_user(@_);
|
||||
last if !$result->{failure};
|
||||
}
|
||||
# Returns the result at the bottom of the stack if they all fail.
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub user_can_create_account {
|
||||
my ($self) = @_;
|
||||
# We return true if any method allows the user to create an account.
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
return 1 if $object->user_can_create_account;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,596 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>,
|
||||
# Bryce Nesbitt <bryce-mozilla@nextbus.com>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Alan Raetz <al_raetz@yahoo.com>
|
||||
# Jacob Steenhagen <jake@actex.net>
|
||||
# Matthew Tuck <matty@chariot.net.au>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Byron Jones <bugzilla@glob.com.au>
|
||||
# Reed Loden <reed@reedloden.com>
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::BugMail;
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Bug;
|
||||
use Bugzilla::Classification;
|
||||
use Bugzilla::Product;
|
||||
use Bugzilla::Component;
|
||||
use Bugzilla::Status;
|
||||
use Bugzilla::Mailer;
|
||||
use Bugzilla::Hook;
|
||||
|
||||
use Date::Parse;
|
||||
use Date::Format;
|
||||
|
||||
use constant FORMAT_TRIPLE => "%19s|%-28s|%-28s";
|
||||
use constant FORMAT_3_SIZE => [19,28,28];
|
||||
use constant FORMAT_DOUBLE => "%19s %-55s";
|
||||
use constant FORMAT_2_SIZE => [19,55];
|
||||
|
||||
use constant BIT_DIRECT => 1;
|
||||
use constant BIT_WATCHING => 2;
|
||||
|
||||
# We use this instead of format because format doesn't deal well with
|
||||
# multi-byte languages.
|
||||
sub multiline_sprintf {
|
||||
my ($format, $args, $sizes) = @_;
|
||||
my @parts;
|
||||
my @my_sizes = @$sizes; # Copy this so we don't modify the input array.
|
||||
foreach my $string (@$args) {
|
||||
my $size = shift @my_sizes;
|
||||
my @pieces = split("\n", wrap_hard($string, $size));
|
||||
push(@parts, \@pieces);
|
||||
}
|
||||
|
||||
my $formatted;
|
||||
while (1) {
|
||||
# Get the first item of each part.
|
||||
my @line = map { shift @$_ } @parts;
|
||||
# If they're all undef, we're done.
|
||||
last if !grep { defined $_ } @line;
|
||||
# Make any single undef item into ''
|
||||
@line = map { defined $_ ? $_ : '' } @line;
|
||||
# And append a formatted line
|
||||
$formatted .= sprintf($format, @line);
|
||||
# Remove trailing spaces, or they become lots of =20's in
|
||||
# quoted-printable emails.
|
||||
$formatted =~ s/\s+$//;
|
||||
$formatted .= "\n";
|
||||
}
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
sub three_columns {
|
||||
return multiline_sprintf(FORMAT_TRIPLE, \@_, FORMAT_3_SIZE);
|
||||
}
|
||||
|
||||
sub relationships {
|
||||
my $ref = RELATIONSHIPS;
|
||||
# Clone it so that we don't modify the constant;
|
||||
my %relationships = %$ref;
|
||||
Bugzilla::Hook::process('bugmail_relationships',
|
||||
{ relationships => \%relationships });
|
||||
return %relationships;
|
||||
}
|
||||
|
||||
# This is a bit of a hack, basically keeping the old system()
|
||||
# cmd line interface. Should clean this up at some point.
|
||||
#
|
||||
# args: bug_id, and an optional hash ref which may have keys for:
|
||||
# changer, owner, qa, reporter, cc
|
||||
# Optional hash contains values of people which will be forced to those
|
||||
# roles when the email is sent.
|
||||
# All the names are email addresses, not userids
|
||||
# values are scalars, except for cc, which is a list
|
||||
sub Send {
|
||||
my ($id, $forced, $params) = (@_);
|
||||
$params ||= {};
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $bug = new Bugzilla::Bug($id);
|
||||
|
||||
# Only used for headers in bugmail for new bugs
|
||||
my @fields = Bugzilla->get_fields({obsolete => 0, mailhead => 1});
|
||||
|
||||
my $start = $bug->lastdiffed;
|
||||
my $end = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
|
||||
|
||||
# Bugzilla::User objects of people in various roles. More than one person
|
||||
# can 'have' a role, if the person in that role has changed, or people are
|
||||
# watching.
|
||||
my @assignees = ($bug->assigned_to);
|
||||
my @qa_contacts = ($bug->qa_contact);
|
||||
|
||||
my @ccs = @{ $bug->cc_users };
|
||||
|
||||
# Include the people passed in as being in particular roles.
|
||||
# This can include people who used to hold those roles.
|
||||
# At this point, we don't care if there are duplicates in these arrays.
|
||||
my $changer = $forced->{'changer'};
|
||||
if ($forced->{'owner'}) {
|
||||
push (@assignees, Bugzilla::User->check($forced->{'owner'}));
|
||||
}
|
||||
|
||||
if ($forced->{'qacontact'}) {
|
||||
push (@qa_contacts, Bugzilla::User->check($forced->{'qacontact'}));
|
||||
}
|
||||
|
||||
if ($forced->{'cc'}) {
|
||||
foreach my $cc (@{$forced->{'cc'}}) {
|
||||
push(@ccs, Bugzilla::User->check($cc));
|
||||
}
|
||||
}
|
||||
|
||||
my @args = ($bug->id);
|
||||
|
||||
# If lastdiffed is NULL, then we don't limit the search on time.
|
||||
my $when_restriction = '';
|
||||
if ($start) {
|
||||
$when_restriction = ' AND bug_when > ? AND bug_when <= ?';
|
||||
push @args, ($start, $end);
|
||||
}
|
||||
|
||||
my $diffs = $dbh->selectall_arrayref(
|
||||
"SELECT profiles.login_name, profiles.realname, fielddefs.description,
|
||||
bugs_activity.bug_when, bugs_activity.removed,
|
||||
bugs_activity.added, bugs_activity.attach_id, fielddefs.name,
|
||||
bugs_activity.comment_id
|
||||
FROM bugs_activity
|
||||
INNER JOIN fielddefs
|
||||
ON fielddefs.id = bugs_activity.fieldid
|
||||
INNER JOIN profiles
|
||||
ON profiles.userid = bugs_activity.who
|
||||
WHERE bugs_activity.bug_id = ?
|
||||
$when_restriction
|
||||
ORDER BY bugs_activity.bug_when", undef, @args);
|
||||
|
||||
my @new_depbugs;
|
||||
my $difftext = "";
|
||||
my $diffheader = "";
|
||||
my @diffparts;
|
||||
my $lastwho = "";
|
||||
my $fullwho;
|
||||
my @changedfields;
|
||||
foreach my $ref (@$diffs) {
|
||||
my ($who, $whoname, $what, $when, $old, $new, $attachid, $fieldname, $comment_id) = (@$ref);
|
||||
my $diffpart = {};
|
||||
if ($who ne $lastwho) {
|
||||
$lastwho = $who;
|
||||
$fullwho = $whoname ? "$whoname <$who>" : $who;
|
||||
$diffheader = "\n$fullwho changed:\n\n";
|
||||
$diffheader .= three_columns("What ", "Removed", "Added");
|
||||
$diffheader .= ('-' x 76) . "\n";
|
||||
}
|
||||
$what =~ s/^(Attachment )?/Attachment #$attachid / if $attachid;
|
||||
if( $fieldname eq 'estimated_time' ||
|
||||
$fieldname eq 'remaining_time' ) {
|
||||
$old = format_time_decimal($old);
|
||||
$new = format_time_decimal($new);
|
||||
}
|
||||
if ($fieldname eq 'dependson') {
|
||||
push(@new_depbugs, grep {$_ =~ /^\d+$/} split(/[\s,]+/, $new));
|
||||
}
|
||||
if ($attachid) {
|
||||
($diffpart->{'isprivate'}) = $dbh->selectrow_array(
|
||||
'SELECT isprivate FROM attachments WHERE attach_id = ?',
|
||||
undef, ($attachid));
|
||||
}
|
||||
if ($fieldname eq 'longdescs.isprivate') {
|
||||
my $comment = Bugzilla::Comment->new($comment_id);
|
||||
my $comment_num = $comment->count;
|
||||
$what =~ s/^(Comment )?/Comment #$comment_num /;
|
||||
$diffpart->{'isprivate'} = $new;
|
||||
}
|
||||
$difftext = three_columns($what, $old, $new);
|
||||
$diffpart->{'header'} = $diffheader;
|
||||
$diffpart->{'fieldname'} = $fieldname;
|
||||
$diffpart->{'text'} = $difftext;
|
||||
push(@diffparts, $diffpart);
|
||||
push(@changedfields, $what);
|
||||
}
|
||||
|
||||
my @depbugs;
|
||||
my $deptext = "";
|
||||
# Do not include data about dependent bugs when they have just been added.
|
||||
# Completely skip checking for dependent bugs on bug creation as all
|
||||
# dependencies bugs will just have been added.
|
||||
if ($start) {
|
||||
my $dep_restriction = "";
|
||||
if (scalar @new_depbugs) {
|
||||
$dep_restriction = "AND bugs_activity.bug_id NOT IN (" .
|
||||
join(", ", @new_depbugs) . ")";
|
||||
}
|
||||
|
||||
my $dependency_diffs = $dbh->selectall_arrayref(
|
||||
"SELECT bugs_activity.bug_id, bugs.short_desc, fielddefs.name,
|
||||
fielddefs.description, bugs_activity.removed,
|
||||
bugs_activity.added
|
||||
FROM bugs_activity
|
||||
INNER JOIN bugs
|
||||
ON bugs.bug_id = bugs_activity.bug_id
|
||||
INNER JOIN dependencies
|
||||
ON bugs_activity.bug_id = dependencies.dependson
|
||||
INNER JOIN fielddefs
|
||||
ON fielddefs.id = bugs_activity.fieldid
|
||||
WHERE dependencies.blocked = ?
|
||||
AND (fielddefs.name = 'bug_status'
|
||||
OR fielddefs.name = 'resolution')
|
||||
$when_restriction
|
||||
$dep_restriction
|
||||
ORDER BY bugs_activity.bug_when, bugs.bug_id", undef, @args);
|
||||
|
||||
my $thisdiff = "";
|
||||
my $lastbug = "";
|
||||
my $interestingchange = 0;
|
||||
foreach my $dependency_diff (@$dependency_diffs) {
|
||||
my ($depbug, $summary, $fieldname, $what, $old, $new) = @$dependency_diff;
|
||||
|
||||
if ($depbug ne $lastbug) {
|
||||
if ($interestingchange) {
|
||||
$deptext .= $thisdiff;
|
||||
}
|
||||
$lastbug = $depbug;
|
||||
$thisdiff =
|
||||
"\nBug $id depends on bug $depbug, which changed state.\n\n" .
|
||||
"Bug $depbug Summary: $summary\n" .
|
||||
correct_urlbase() . "show_bug.cgi?id=$depbug\n\n";
|
||||
$thisdiff .= three_columns("What ", "Old Value", "New Value");
|
||||
$thisdiff .= ('-' x 76) . "\n";
|
||||
$interestingchange = 0;
|
||||
}
|
||||
$thisdiff .= three_columns($what, $old, $new);
|
||||
if ($fieldname eq 'bug_status'
|
||||
&& is_open_state($old) ne is_open_state($new))
|
||||
{
|
||||
$interestingchange = 1;
|
||||
}
|
||||
push(@depbugs, $depbug);
|
||||
}
|
||||
|
||||
if ($interestingchange) {
|
||||
$deptext .= $thisdiff;
|
||||
}
|
||||
$deptext = trim($deptext);
|
||||
|
||||
if ($deptext) {
|
||||
my $diffpart = {};
|
||||
$diffpart->{'text'} = "\n" . trim($deptext);
|
||||
push(@diffparts, $diffpart);
|
||||
}
|
||||
}
|
||||
|
||||
my $comments = $bug->comments({ after => $start, to => $end });
|
||||
# Skip empty comments.
|
||||
@$comments = grep { $_->type || $_->body =~ /\S/ } @$comments;
|
||||
|
||||
###########################################################################
|
||||
# Start of email filtering code
|
||||
###########################################################################
|
||||
|
||||
# A user_id => roles hash to keep track of people.
|
||||
my %recipients;
|
||||
my %watching;
|
||||
|
||||
# Now we work out all the people involved with this bug, and note all of
|
||||
# the relationships in a hash. The keys are userids, the values are an
|
||||
# array of role constants.
|
||||
|
||||
# CCs
|
||||
$recipients{$_->id}->{+REL_CC} = BIT_DIRECT foreach (@ccs);
|
||||
|
||||
# Reporter (there's only ever one)
|
||||
$recipients{$bug->reporter->id}->{+REL_REPORTER} = BIT_DIRECT;
|
||||
|
||||
# QA Contact
|
||||
if (Bugzilla->params->{'useqacontact'}) {
|
||||
foreach (@qa_contacts) {
|
||||
# QA Contact can be blank; ignore it if so.
|
||||
$recipients{$_->id}->{+REL_QA} = BIT_DIRECT if $_;
|
||||
}
|
||||
}
|
||||
|
||||
# Assignee
|
||||
$recipients{$_->id}->{+REL_ASSIGNEE} = BIT_DIRECT foreach (@assignees);
|
||||
|
||||
# The last relevant set of people are those who are being removed from
|
||||
# their roles in this change. We get their names out of the diffs.
|
||||
foreach my $ref (@$diffs) {
|
||||
my ($who, $whoname, $what, $when, $old, $new) = (@$ref);
|
||||
if ($old) {
|
||||
# You can't stop being the reporter, so we don't check that
|
||||
# relationship here.
|
||||
# Ignore people whose user account has been deleted or renamed.
|
||||
if ($what eq "CC") {
|
||||
foreach my $cc_user (split(/[\s,]+/, $old)) {
|
||||
my $uid = login_to_id($cc_user);
|
||||
$recipients{$uid}->{+REL_CC} = BIT_DIRECT if $uid;
|
||||
}
|
||||
}
|
||||
elsif ($what eq "QAContact") {
|
||||
my $uid = login_to_id($old);
|
||||
$recipients{$uid}->{+REL_QA} = BIT_DIRECT if $uid;
|
||||
}
|
||||
elsif ($what eq "AssignedTo") {
|
||||
my $uid = login_to_id($old);
|
||||
$recipients{$uid}->{+REL_ASSIGNEE} = BIT_DIRECT if $uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bugzilla::Hook::process('bugmail_recipients',
|
||||
{ bug => $bug, recipients => \%recipients,
|
||||
diffs => $diffs });
|
||||
|
||||
# Find all those user-watching anyone on the current list, who is not
|
||||
# on it already themselves.
|
||||
my $involved = join(",", keys %recipients);
|
||||
|
||||
my $userwatchers =
|
||||
$dbh->selectall_arrayref("SELECT watcher, watched FROM watch
|
||||
WHERE watched IN ($involved)");
|
||||
|
||||
# Mark these people as having the role of the person they are watching
|
||||
foreach my $watch (@$userwatchers) {
|
||||
while (my ($role, $bits) = each %{$recipients{$watch->[1]}}) {
|
||||
$recipients{$watch->[0]}->{$role} |= BIT_WATCHING
|
||||
if $bits & BIT_DIRECT;
|
||||
}
|
||||
push(@{$watching{$watch->[0]}}, $watch->[1]);
|
||||
}
|
||||
|
||||
# Global watcher
|
||||
my @watchers = split(/[,\s]+/, Bugzilla->params->{'globalwatchers'});
|
||||
foreach (@watchers) {
|
||||
my $watcher_id = login_to_id($_);
|
||||
next unless $watcher_id;
|
||||
$recipients{$watcher_id}->{+REL_GLOBAL_WATCHER} = BIT_DIRECT;
|
||||
}
|
||||
|
||||
# We now have a complete set of all the users, and their relationships to
|
||||
# the bug in question. However, we are not necessarily going to mail them
|
||||
# all - there are preferences, permissions checks and all sorts to do yet.
|
||||
my @sent;
|
||||
my @excluded;
|
||||
|
||||
# The email client will display the Date: header in the desired timezone,
|
||||
# so we can always use UTC here.
|
||||
my $date = $params->{dep_only} ? $end : $bug->delta_ts;
|
||||
$date = format_time($date, '%a, %d %b %Y %T %z', 'UTC');
|
||||
|
||||
foreach my $user_id (keys %recipients) {
|
||||
my %rels_which_want;
|
||||
my $sent_mail = 0;
|
||||
my $user = new Bugzilla::User($user_id);
|
||||
# Deleted users must be excluded.
|
||||
next unless $user;
|
||||
|
||||
if ($user->can_see_bug($id)) {
|
||||
# Go through each role the user has and see if they want mail in
|
||||
# that role.
|
||||
foreach my $relationship (keys %{$recipients{$user_id}}) {
|
||||
if ($user->wants_bug_mail($id,
|
||||
$relationship,
|
||||
$diffs,
|
||||
$comments,
|
||||
$deptext,
|
||||
$changer,
|
||||
!$start))
|
||||
{
|
||||
$rels_which_want{$relationship} =
|
||||
$recipients{$user_id}->{$relationship};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar(%rels_which_want)) {
|
||||
# So the user exists, can see the bug, and wants mail in at least
|
||||
# one role. But do we want to send it to them?
|
||||
|
||||
# We shouldn't send mail if this is a dependency mail (i.e. there
|
||||
# is something in @depbugs), and any of the depending bugs are not
|
||||
# visible to the user. This is to avoid leaking the summaries of
|
||||
# confidential bugs.
|
||||
my $dep_ok = 1;
|
||||
foreach my $dep_id (@depbugs) {
|
||||
if (!$user->can_see_bug($dep_id)) {
|
||||
$dep_ok = 0;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# Make sure the user isn't in the nomail list, and the insider and
|
||||
# dep checks passed.
|
||||
if ($user->email_enabled && $dep_ok) {
|
||||
# OK, OK, if we must. Email the user.
|
||||
$sent_mail = sendMail(
|
||||
{ to => $user,
|
||||
fields => \@fields,
|
||||
bug => $bug,
|
||||
comments => $comments,
|
||||
is_new => !$start,
|
||||
date => $date,
|
||||
changer => $changer,
|
||||
watchers => exists $watching{$user_id} ?
|
||||
$watching{$user_id} : undef,
|
||||
diff_parts => \@diffparts,
|
||||
rels_which_want => \%rels_which_want,
|
||||
changed_fields => \@changedfields,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ($sent_mail) {
|
||||
push(@sent, $user->login);
|
||||
}
|
||||
else {
|
||||
push(@excluded, $user->login);
|
||||
}
|
||||
}
|
||||
|
||||
$dbh->do('UPDATE bugs SET lastdiffed = ? WHERE bug_id = ?',
|
||||
undef, ($end, $id));
|
||||
$bug->{lastdiffed} = $end;
|
||||
|
||||
return {'sent' => \@sent, 'excluded' => \@excluded};
|
||||
}
|
||||
|
||||
sub sendMail {
|
||||
my $params = shift;
|
||||
|
||||
my $user = $params->{to};
|
||||
my @fields = @{ $params->{fields} };
|
||||
my $bug = $params->{bug};
|
||||
my @send_comments = @{ $params->{comments} };
|
||||
my $isnew = $params->{is_new};
|
||||
my $date = $params->{date};
|
||||
my $changer = $params->{changer};
|
||||
my $watchingRef = $params->{watchers};
|
||||
my @diffparts = @{ $params->{diff_parts} };
|
||||
my $relRef = $params->{rels_which_want};
|
||||
my @changed_fields = @{ $params->{changed_fields} };
|
||||
|
||||
# Build difftext (the actions) by verifying the user should see them
|
||||
my $difftext = "";
|
||||
my $diffheader = "";
|
||||
my $add_diff;
|
||||
|
||||
foreach my $diff (@diffparts) {
|
||||
$add_diff = 0;
|
||||
|
||||
if (exists($diff->{'fieldname'}) &&
|
||||
($diff->{'fieldname'} eq 'estimated_time' ||
|
||||
$diff->{'fieldname'} eq 'remaining_time' ||
|
||||
$diff->{'fieldname'} eq 'work_time' ||
|
||||
$diff->{'fieldname'} eq 'deadline'))
|
||||
{
|
||||
$add_diff = 1 if $user->is_timetracker;
|
||||
} elsif ($diff->{'isprivate'}
|
||||
&& !$user->is_insider)
|
||||
{
|
||||
$add_diff = 0;
|
||||
} else {
|
||||
$add_diff = 1;
|
||||
}
|
||||
|
||||
if ($add_diff) {
|
||||
if (exists($diff->{'header'}) &&
|
||||
($diffheader ne $diff->{'header'})) {
|
||||
$diffheader = $diff->{'header'};
|
||||
$difftext .= $diffheader;
|
||||
}
|
||||
$difftext .= $diff->{'text'};
|
||||
}
|
||||
}
|
||||
|
||||
if (!$user->is_insider) {
|
||||
@send_comments = grep { !$_->is_private } @send_comments;
|
||||
}
|
||||
|
||||
if ($difftext eq "" && !scalar(@send_comments) && !$isnew) {
|
||||
# Whoops, no differences!
|
||||
return 0;
|
||||
}
|
||||
|
||||
my $diffs = $difftext;
|
||||
# Remove extra newlines.
|
||||
$diffs =~ s/^\n+//s; $diffs =~ s/\n+$//s;
|
||||
if ($isnew) {
|
||||
my $head = "";
|
||||
foreach my $field (@fields) {
|
||||
my $name = $field->name;
|
||||
my $value = $bug->$name;
|
||||
|
||||
if (ref $value eq 'ARRAY') {
|
||||
$value = join(', ', @$value);
|
||||
}
|
||||
elsif (ref $value && $value->isa('Bugzilla::User')) {
|
||||
$value = $value->login;
|
||||
}
|
||||
elsif (ref $value && $value->isa('Bugzilla::Object')) {
|
||||
$value = $value->name;
|
||||
}
|
||||
elsif ($name eq 'estimated_time') {
|
||||
$value = ($value == 0) ? 0 : format_time_decimal($value);
|
||||
}
|
||||
elsif ($name eq 'deadline') {
|
||||
$value = time2str("%Y-%m-%d", str2time($value)) if $value;
|
||||
}
|
||||
|
||||
# If there isn't anything to show, don't include this header.
|
||||
next unless $value;
|
||||
# Only send estimated_time if it is enabled and the user is in the group.
|
||||
if (($name ne 'estimated_time' && $name ne 'deadline') || $user->is_timetracker) {
|
||||
my $desc = $field->description;
|
||||
$head .= multiline_sprintf(FORMAT_DOUBLE, ["$desc:", $value],
|
||||
FORMAT_2_SIZE);
|
||||
}
|
||||
}
|
||||
$diffs = $head . ($difftext ? "\n\n" : "") . $diffs;
|
||||
}
|
||||
|
||||
my (@reasons, @reasons_watch);
|
||||
while (my ($relationship, $bits) = each %{$relRef}) {
|
||||
push(@reasons, $relationship) if ($bits & BIT_DIRECT);
|
||||
push(@reasons_watch, $relationship) if ($bits & BIT_WATCHING);
|
||||
}
|
||||
|
||||
my %relationships = relationships();
|
||||
my @headerrel = map { $relationships{$_} } @reasons;
|
||||
my @watchingrel = map { $relationships{$_} } @reasons_watch;
|
||||
push(@headerrel, 'None') unless @headerrel;
|
||||
push(@watchingrel, 'None') unless @watchingrel;
|
||||
push @watchingrel, map { user_id_to_login($_) } @$watchingRef;
|
||||
|
||||
my $vars = {
|
||||
isnew => $isnew,
|
||||
date => $date,
|
||||
to_user => $user,
|
||||
bug => $bug,
|
||||
changedfields => \@changed_fields,
|
||||
reasons => \@reasons,
|
||||
reasons_watch => \@reasons_watch,
|
||||
reasonsheader => join(" ", @headerrel),
|
||||
reasonswatchheader => join(" ", @watchingrel),
|
||||
changer => $changer,
|
||||
diffs => $diffs,
|
||||
new_comments => \@send_comments,
|
||||
threadingmarker => build_thread_marker($bug->id, $user->id, $isnew),
|
||||
};
|
||||
|
||||
my $msg;
|
||||
my $template = Bugzilla->template_inner($user->settings->{'lang'}->{'value'});
|
||||
$template->process("email/newchangedmail.txt.tmpl", $vars, \$msg)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
MessageToMTA($msg);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,626 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Byron Jones <bugzilla@glob.com.au>
|
||||
# Marc Schumann <wurblzap@gmail.com>
|
||||
|
||||
package Bugzilla::CGI;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Search::Recent;
|
||||
|
||||
use File::Basename;
|
||||
|
||||
BEGIN {
|
||||
if (ON_WINDOWS) {
|
||||
# Help CGI find the correct temp directory as the default list
|
||||
# isn't Windows friendly (Bug 248988)
|
||||
$ENV{'TMPDIR'} = $ENV{'TEMP'} || $ENV{'TMP'} || "$ENV{'WINDIR'}\\TEMP";
|
||||
}
|
||||
}
|
||||
|
||||
use CGI qw(-no_xhtml -oldstyle_urls :private_tempfiles
|
||||
:unique_headers SERVER_PUSH);
|
||||
use base qw(CGI);
|
||||
|
||||
# We need to disable output buffering - see bug 179174
|
||||
$| = 1;
|
||||
|
||||
# Ignore SIGTERM and SIGPIPE - this prevents DB corruption. If the user closes
|
||||
# their browser window while a script is running, the web server sends these
|
||||
# signals, and we don't want to die half way through a write.
|
||||
$::SIG{TERM} = 'IGNORE';
|
||||
$::SIG{PIPE} = 'IGNORE';
|
||||
|
||||
# CGI.pm uses AUTOLOAD, but explicitly defines a DESTROY sub.
|
||||
# We need to do so, too, otherwise perl dies when the object is destroyed
|
||||
# and we don't have a DESTROY method (because CGI.pm's AUTOLOAD will |die|
|
||||
# on getting an unknown sub to try to call)
|
||||
sub DESTROY {
|
||||
my $self = shift;
|
||||
$self->SUPER::DESTROY(@_);
|
||||
};
|
||||
|
||||
sub new {
|
||||
my ($invocant, @args) = @_;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
|
||||
my $self = $class->SUPER::new(@args);
|
||||
|
||||
# Make sure our outgoing cookie list is empty on each invocation
|
||||
$self->{Bugzilla_cookie_list} = [];
|
||||
|
||||
# Send appropriate charset
|
||||
$self->charset(Bugzilla->params->{'utf8'} ? 'UTF-8' : '');
|
||||
|
||||
# Redirect to urlbase/sslbase if we are not viewing an attachment.
|
||||
my $script = basename($0);
|
||||
if ($self->url_is_attachment_base and $script ne 'attachment.cgi') {
|
||||
$self->redirect_to_urlbase();
|
||||
}
|
||||
|
||||
# Check for errors
|
||||
# All of the Bugzilla code wants to do this, so do it here instead of
|
||||
# in each script
|
||||
|
||||
my $err = $self->cgi_error;
|
||||
|
||||
if ($err) {
|
||||
# Note that this error block is only triggered by CGI.pm for malformed
|
||||
# multipart requests, and so should never happen unless there is a
|
||||
# browser bug.
|
||||
|
||||
print $self->header(-status => $err);
|
||||
|
||||
# ThrowCodeError wants to print the header, so it grabs Bugzilla->cgi
|
||||
# which creates a new Bugzilla::CGI object, which fails again, which
|
||||
# ends up here, and calls ThrowCodeError, and then recurses forever.
|
||||
# So don't use it.
|
||||
# In fact, we can't use templates at all, because we need a CGI object
|
||||
# to determine the template lang as well as the current url (from the
|
||||
# template)
|
||||
# Since this is an internal error which indicates a severe browser bug,
|
||||
# just die.
|
||||
die "CGI parsing error: $err";
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
# We want this sorted plus the ability to exclude certain params
|
||||
sub canonicalise_query {
|
||||
my ($self, @exclude) = @_;
|
||||
|
||||
$self->convert_old_params();
|
||||
# Reconstruct the URL by concatenating the sorted param=value pairs
|
||||
my @parameters;
|
||||
foreach my $key (sort($self->param())) {
|
||||
# Leave this key out if it's in the exclude list
|
||||
next if grep { $_ eq $key } @exclude;
|
||||
|
||||
# Remove the Boolean Charts for standard query.cgi fields
|
||||
# They are listed in the query URL already
|
||||
next if $key =~ /^(field|type|value)(-\d+){3}$/;
|
||||
|
||||
my $esc_key = url_quote($key);
|
||||
|
||||
foreach my $value ($self->param($key)) {
|
||||
if (defined($value)) {
|
||||
my $esc_value = url_quote($value);
|
||||
|
||||
push(@parameters, "$esc_key=$esc_value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return join("&", @parameters);
|
||||
}
|
||||
|
||||
sub convert_old_params {
|
||||
my $self = shift;
|
||||
|
||||
# bugidtype is now bug_id_type.
|
||||
if ($self->param('bugidtype')) {
|
||||
my $value = $self->param('bugidtype') eq 'exclude' ? 'nowords' : 'anyexact';
|
||||
$self->param('bug_id_type', $value);
|
||||
$self->delete('bugidtype');
|
||||
}
|
||||
}
|
||||
|
||||
sub clean_search_url {
|
||||
my $self = shift;
|
||||
# Delete any empty URL parameter.
|
||||
my @cgi_params = $self->param;
|
||||
|
||||
foreach my $param (@cgi_params) {
|
||||
if (defined $self->param($param) && $self->param($param) eq '') {
|
||||
$self->delete($param);
|
||||
$self->delete("${param}_type");
|
||||
}
|
||||
|
||||
# Boolean Chart stuff is empty if it's "noop"
|
||||
if ($param =~ /\d-\d-\d/ && defined $self->param($param)
|
||||
&& $self->param($param) eq 'noop')
|
||||
{
|
||||
$self->delete($param);
|
||||
}
|
||||
}
|
||||
|
||||
# Delete leftovers from the login form
|
||||
$self->delete('Bugzilla_remember', 'GoAheadAndLogIn');
|
||||
|
||||
foreach my $num (1,2,3) {
|
||||
# If there's no value in the email field, delete the related fields.
|
||||
if (!$self->param("email$num")) {
|
||||
foreach my $field (qw(type assigned_to reporter qa_contact cc longdesc)) {
|
||||
$self->delete("email$field$num");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# chfieldto is set to "Now" by default in query.cgi. But if none
|
||||
# of the other chfield parameters are set, it's meaningless.
|
||||
if (!defined $self->param('chfieldfrom') && !$self->param('chfield')
|
||||
&& !defined $self->param('chfieldvalue') && $self->param('chfieldto')
|
||||
&& lc($self->param('chfieldto')) eq 'now')
|
||||
{
|
||||
$self->delete('chfieldto');
|
||||
}
|
||||
|
||||
# cmdtype "doit" is the default from query.cgi, but it's only meaningful
|
||||
# if there's a remtype parameter.
|
||||
if (defined $self->param('cmdtype') && $self->param('cmdtype') eq 'doit'
|
||||
&& !defined $self->param('remtype'))
|
||||
{
|
||||
$self->delete('cmdtype');
|
||||
}
|
||||
|
||||
# "Reuse same sort as last time" is actually the default, so we don't
|
||||
# need it in the URL.
|
||||
if ($self->param('order')
|
||||
&& $self->param('order') eq 'Reuse same sort as last time')
|
||||
{
|
||||
$self->delete('order');
|
||||
}
|
||||
|
||||
# list_id is added in buglist.cgi after calling clean_search_url,
|
||||
# and doesn't need to be saved in saved searches.
|
||||
$self->delete('list_id');
|
||||
|
||||
# And now finally, if query_format is our only parameter, that
|
||||
# really means we have no parameters, so we should delete query_format.
|
||||
if ($self->param('query_format') && scalar($self->param()) == 1) {
|
||||
$self->delete('query_format');
|
||||
}
|
||||
}
|
||||
|
||||
# Overwrite to ensure nph doesn't get set, and unset HEADERS_ONCE
|
||||
sub multipart_init {
|
||||
my $self = shift;
|
||||
|
||||
# Keys are case-insensitive, map to lowercase
|
||||
my %args = @_;
|
||||
my %param;
|
||||
foreach my $key (keys %args) {
|
||||
$param{lc $key} = $args{$key};
|
||||
}
|
||||
|
||||
# Set the MIME boundary and content-type
|
||||
my $boundary = $param{'-boundary'}
|
||||
|| '------- =_' . generate_random_password(16);
|
||||
delete $param{'-boundary'};
|
||||
$self->{'separator'} = "\r\n--$boundary\r\n";
|
||||
$self->{'final_separator'} = "\r\n--$boundary--\r\n";
|
||||
$param{'-type'} = SERVER_PUSH($boundary);
|
||||
|
||||
# Note: CGI.pm::multipart_init up to v3.04 explicitly set nph to 0
|
||||
# CGI.pm::multipart_init v3.05 explicitly sets nph to 1
|
||||
# CGI.pm's header() sets nph according to a param or $CGI::NPH, which
|
||||
# is the desired behaviour.
|
||||
|
||||
return $self->header(
|
||||
%param,
|
||||
) . "WARNING: YOUR BROWSER DOESN'T SUPPORT THIS SERVER-PUSH TECHNOLOGY." . $self->multipart_end;
|
||||
}
|
||||
|
||||
# Have to add the cookies in.
|
||||
sub multipart_start {
|
||||
my $self = shift;
|
||||
|
||||
my %args = @_;
|
||||
|
||||
# CGI.pm::multipart_start doesn't honour its own charset information, so
|
||||
# we do it ourselves here
|
||||
if (defined $self->charset() && defined $args{-type}) {
|
||||
# Remove any existing charset specifier
|
||||
$args{-type} =~ s/;.*$//;
|
||||
# and add the specified one
|
||||
$args{-type} .= '; charset=' . $self->charset();
|
||||
}
|
||||
|
||||
my $headers = $self->SUPER::multipart_start(%args);
|
||||
# Eliminate the one extra CRLF at the end.
|
||||
$headers =~ s/$CGI::CRLF$//;
|
||||
# Add the cookies. We have to do it this way instead of
|
||||
# passing them to multpart_start, because CGI.pm's multipart_start
|
||||
# doesn't understand a '-cookie' argument pointing to an arrayref.
|
||||
foreach my $cookie (@{$self->{Bugzilla_cookie_list}}) {
|
||||
$headers .= "Set-Cookie: ${cookie}${CGI::CRLF}";
|
||||
}
|
||||
$headers .= $CGI::CRLF;
|
||||
return $headers;
|
||||
}
|
||||
|
||||
# Override header so we can add the cookies in
|
||||
sub header {
|
||||
my $self = shift;
|
||||
|
||||
# If there's only one parameter, then it's a Content-Type.
|
||||
if (scalar(@_) == 1) {
|
||||
# Since we're adding parameters below, we have to name it.
|
||||
unshift(@_, '-type' => shift(@_));
|
||||
}
|
||||
|
||||
# Add the cookies in if we have any
|
||||
if (scalar(@{$self->{Bugzilla_cookie_list}})) {
|
||||
unshift(@_, '-cookie' => $self->{Bugzilla_cookie_list});
|
||||
}
|
||||
|
||||
# Add Strict-Transport-Security (STS) header if this response
|
||||
# is over SSL and the strict_transport_security param is turned on.
|
||||
if ($self->https && !$self->url_is_attachment_base
|
||||
&& Bugzilla->params->{'strict_transport_security'} ne 'off')
|
||||
{
|
||||
my $sts_opts = 'max-age=' . MAX_STS_AGE;
|
||||
if (Bugzilla->params->{'strict_transport_security'}
|
||||
eq 'include_subdomains')
|
||||
{
|
||||
$sts_opts .= '; includeSubDomains';
|
||||
}
|
||||
unshift(@_, '-strict_transport_security' => $sts_opts);
|
||||
}
|
||||
|
||||
# Add X-Frame-Options header to prevent framing and subsequent
|
||||
# possible clickjacking problems.
|
||||
unless ($self->url_is_attachment_base) {
|
||||
unshift(@_, '-x_frame_options' => 'SAMEORIGIN');
|
||||
}
|
||||
|
||||
return $self->SUPER::header(@_) || "";
|
||||
}
|
||||
|
||||
sub param {
|
||||
my $self = shift;
|
||||
|
||||
# When we are just requesting the value of a parameter...
|
||||
if (scalar(@_) == 1) {
|
||||
my @result = $self->SUPER::param(@_);
|
||||
|
||||
# Also look at the URL parameters, after we look at the POST
|
||||
# parameters. This is to allow things like login-form submissions
|
||||
# with URL parameters in the form's "target" attribute.
|
||||
if (!scalar(@result)
|
||||
&& $self->request_method && $self->request_method eq 'POST')
|
||||
{
|
||||
# Some servers fail to set the QUERY_STRING parameter, which
|
||||
# causes undef issues
|
||||
$ENV{'QUERY_STRING'} = '' unless exists $ENV{'QUERY_STRING'};
|
||||
@result = $self->SUPER::url_param(@_);
|
||||
}
|
||||
|
||||
# Fix UTF-8-ness of input parameters.
|
||||
if (Bugzilla->params->{'utf8'}) {
|
||||
@result = map { _fix_utf8($_) } @result;
|
||||
}
|
||||
|
||||
return wantarray ? @result : $result[0];
|
||||
}
|
||||
# And for various other functions in CGI.pm, we need to correctly
|
||||
# return the URL parameters in addition to the POST parameters when
|
||||
# asked for the list of parameters.
|
||||
elsif (!scalar(@_) && $self->request_method
|
||||
&& $self->request_method eq 'POST')
|
||||
{
|
||||
my @post_params = $self->SUPER::param;
|
||||
my @url_params = $self->url_param;
|
||||
my %params = map { $_ => 1 } (@post_params, @url_params);
|
||||
return keys %params;
|
||||
}
|
||||
|
||||
return $self->SUPER::param(@_);
|
||||
}
|
||||
|
||||
sub _fix_utf8 {
|
||||
my $input = shift;
|
||||
# The is_utf8 is here in case CGI gets smart about utf8 someday.
|
||||
utf8::decode($input) if defined $input && !ref $input && !utf8::is_utf8($input);
|
||||
return $input;
|
||||
}
|
||||
|
||||
sub should_set {
|
||||
my ($self, $param) = @_;
|
||||
my $set = (defined $self->param($param)
|
||||
or defined $self->param("defined_$param"))
|
||||
? 1 : 0;
|
||||
return $set;
|
||||
}
|
||||
|
||||
# The various parts of Bugzilla which create cookies don't want to have to
|
||||
# pass them around to all of the callers. Instead, store them locally here,
|
||||
# and then output as required from |header|.
|
||||
sub send_cookie {
|
||||
my $self = shift;
|
||||
|
||||
# Move the param list into a hash for easier handling.
|
||||
my %paramhash;
|
||||
my @paramlist;
|
||||
my ($key, $value);
|
||||
while ($key = shift) {
|
||||
$value = shift;
|
||||
$paramhash{$key} = $value;
|
||||
}
|
||||
|
||||
# Complain if -value is not given or empty (bug 268146).
|
||||
if (!exists($paramhash{'-value'}) || !$paramhash{'-value'}) {
|
||||
ThrowCodeError('cookies_need_value');
|
||||
}
|
||||
|
||||
# Add the default path and the domain in.
|
||||
$paramhash{'-path'} = Bugzilla->params->{'cookiepath'};
|
||||
$paramhash{'-domain'} = Bugzilla->params->{'cookiedomain'}
|
||||
if Bugzilla->params->{'cookiedomain'};
|
||||
|
||||
# Move the param list back into an array for the call to cookie().
|
||||
foreach (keys(%paramhash)) {
|
||||
unshift(@paramlist, $_ => $paramhash{$_});
|
||||
}
|
||||
|
||||
push(@{$self->{'Bugzilla_cookie_list'}}, $self->cookie(@paramlist));
|
||||
}
|
||||
|
||||
# Cookies are removed by setting an expiry date in the past.
|
||||
# This method is a send_cookie wrapper doing exactly this.
|
||||
sub remove_cookie {
|
||||
my $self = shift;
|
||||
my ($cookiename) = (@_);
|
||||
|
||||
# Expire the cookie, giving a non-empty dummy value (bug 268146).
|
||||
$self->send_cookie('-name' => $cookiename,
|
||||
'-expires' => 'Tue, 15-Sep-1998 21:49:00 GMT',
|
||||
'-value' => 'X');
|
||||
}
|
||||
|
||||
# This helps implement Bugzilla::Search::Recent, and also shortens search
|
||||
# URLs that get POSTed to buglist.cgi.
|
||||
sub redirect_search_url {
|
||||
my $self = shift;
|
||||
# If we're retreiving an old list, we never need to redirect or
|
||||
# do anything related to Bugzilla::Search::Recent.
|
||||
return if $self->param('regetlastlist');
|
||||
|
||||
my $user = Bugzilla->user;
|
||||
|
||||
if ($user->id) {
|
||||
# There are two conditions that could happen here--we could get a URL
|
||||
# with no list id, and we could get a URL with a list_id that isn't
|
||||
# ours.
|
||||
my $list_id = $self->param('list_id');
|
||||
my $last_search;
|
||||
if ($list_id) {
|
||||
# If we have a valid list_id, no need to redirect or clean.
|
||||
return if Bugzilla::Search::Recent->check_quietly(
|
||||
{ id => $list_id });
|
||||
}
|
||||
}
|
||||
elsif ($self->request_method ne 'POST') {
|
||||
# Logged-out users who do a GET don't get a list_id, don't get
|
||||
# their URLs cleaned, and don't get redirected.
|
||||
return;
|
||||
}
|
||||
|
||||
$self->clean_search_url();
|
||||
|
||||
# Make sure we have params still after cleaning otherwise we
|
||||
# do not want to store a list_id for an empty search.
|
||||
if ($user->id && $self->param) {
|
||||
# Insert a placeholder Bugzilla::Search::Recent, so that we know what
|
||||
# the id of the resulting search will be. This is then pulled out
|
||||
# of the Referer header when viewing show_bug.cgi to know what
|
||||
# bug list we came from.
|
||||
my $recent_search = Bugzilla::Search::Recent->create_placeholder;
|
||||
$self->param('list_id', $recent_search->id);
|
||||
}
|
||||
|
||||
# GET requests that lacked a list_id are always redirected. POST requests
|
||||
# are only redirected if they're under the CGI_URI_LIMIT though.
|
||||
my $uri_length = length($self->self_url());
|
||||
if ($self->request_method() ne 'POST' or $uri_length < CGI_URI_LIMIT) {
|
||||
print $self->redirect(-url => $self->self_url());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
sub redirect_to_https {
|
||||
my $self = shift;
|
||||
my $sslbase = Bugzilla->params->{'sslbase'};
|
||||
# If this is a POST, we don't want ?POSTDATA in the query string.
|
||||
# We expect the client to re-POST, which may be a violation of
|
||||
# the HTTP spec, but the only time we're expecting it often is
|
||||
# in the WebService, and WebService clients usually handle this
|
||||
# correctly.
|
||||
$self->delete('POSTDATA');
|
||||
my $url = $sslbase . $self->url('-path_info' => 1, '-query' => 1,
|
||||
'-relative' => 1);
|
||||
|
||||
# XML-RPC clients (SOAP::Lite at least) require a 301 to redirect properly
|
||||
# and do not work with 302. Our redirect really is permanent anyhow, so
|
||||
# it doesn't hurt to make it a 301.
|
||||
print $self->redirect(-location => $url, -status => 301);
|
||||
|
||||
# When using XML-RPC with mod_perl, we need the headers sent immediately.
|
||||
$self->r->rflush if $ENV{MOD_PERL};
|
||||
exit;
|
||||
}
|
||||
|
||||
# Redirect to the urlbase version of the current URL.
|
||||
sub redirect_to_urlbase {
|
||||
my $self = shift;
|
||||
my $path = $self->url('-path_info' => 1, '-query' => 1, '-relative' => 1);
|
||||
print $self->redirect('-location' => correct_urlbase() . $path);
|
||||
exit;
|
||||
}
|
||||
|
||||
sub url_is_attachment_base {
|
||||
my ($self, $id) = @_;
|
||||
return 0 if !use_attachbase() or !i_am_cgi();
|
||||
my $attach_base = Bugzilla->params->{'attachment_base'};
|
||||
# If we're passed an id, we only want one specific attachment base
|
||||
# for a particular bug. If we're not passed an ID, we just want to
|
||||
# know if our current URL matches the attachment_base *pattern*.
|
||||
my $regex;
|
||||
if ($id) {
|
||||
$attach_base =~ s/\%bugid\%/$id/;
|
||||
$regex = quotemeta($attach_base);
|
||||
}
|
||||
else {
|
||||
# In this circumstance we run quotemeta first because we need to
|
||||
# insert an active regex meta-character afterward.
|
||||
$regex = quotemeta($attach_base);
|
||||
$regex =~ s/\\\%bugid\\\%/\\d+/;
|
||||
}
|
||||
$regex = "^$regex";
|
||||
return ($self->self_url =~ $regex) ? 1 : 0;
|
||||
}
|
||||
|
||||
##########################
|
||||
# Vars TIEHASH Interface #
|
||||
##########################
|
||||
|
||||
# Fix the TIEHASH interface (scalar $cgi->Vars) to return and accept
|
||||
# arrayrefs.
|
||||
sub STORE {
|
||||
my $self = shift;
|
||||
my ($param, $value) = @_;
|
||||
if (defined $value and ref $value eq 'ARRAY') {
|
||||
return $self->param(-name => $param, -value => $value);
|
||||
}
|
||||
return $self->SUPER::STORE(@_);
|
||||
}
|
||||
|
||||
sub FETCH {
|
||||
my ($self, $param) = @_;
|
||||
return $self if $param eq 'CGI'; # CGI.pm did this, so we do too.
|
||||
my @result = $self->param($param);
|
||||
return undef if !scalar(@result);
|
||||
return $result[0] if scalar(@result) == 1;
|
||||
return \@result;
|
||||
}
|
||||
|
||||
# For the Vars TIEHASH interface: the normal CGI.pm DELETE doesn't return
|
||||
# the value deleted, but Perl's "delete" expects that value.
|
||||
sub DELETE {
|
||||
my ($self, $param) = @_;
|
||||
my $value = $self->FETCH($param);
|
||||
$self->delete($param);
|
||||
return $value;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::CGI - CGI handling for Bugzilla
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::CGI;
|
||||
|
||||
my $cgi = new Bugzilla::CGI();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This package inherits from the standard CGI module, to provide additional
|
||||
Bugzilla-specific functionality. In general, see L<the CGI.pm docs|CGI> for
|
||||
documention.
|
||||
|
||||
=head1 CHANGES FROM L<CGI.PM|CGI>
|
||||
|
||||
Bugzilla::CGI has some differences from L<CGI.pm|CGI>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<cgi_error> is automatically checked
|
||||
|
||||
After creating the CGI object, C<Bugzilla::CGI> automatically checks
|
||||
I<cgi_error>, and throws a CodeError if a problem is detected.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ADDITIONAL FUNCTIONS
|
||||
|
||||
I<Bugzilla::CGI> also includes additional functions.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<canonicalise_query(@exclude)>
|
||||
|
||||
This returns a sorted string of the parameters, suitable for use in a url.
|
||||
Values in C<@exclude> are not included in the result.
|
||||
|
||||
=item C<send_cookie>
|
||||
|
||||
This routine is identical to the cookie generation part of CGI.pm's C<cookie>
|
||||
routine, except that it knows about Bugzilla's cookie_path and cookie_domain
|
||||
parameters and takes them into account if necessary.
|
||||
This should be used by all Bugzilla code (instead of C<cookie> or the C<-cookie>
|
||||
argument to C<header>), so that under mod_perl the headers can be sent
|
||||
correctly, using C<print> or the mod_perl APIs as appropriate.
|
||||
|
||||
To remove (expire) a cookie, use C<remove_cookie>.
|
||||
|
||||
=item C<remove_cookie>
|
||||
|
||||
This is a wrapper around send_cookie, setting an expiry date in the past,
|
||||
effectively removing the cookie.
|
||||
|
||||
As its only argument, it takes the name of the cookie to expire.
|
||||
|
||||
=item C<redirect_to_https>
|
||||
|
||||
This routine redirects the client to the https version of the page that
|
||||
they're looking at, using the C<sslbase> parameter for the redirection.
|
||||
|
||||
Generally you should use L<Bugzilla::Util/do_ssl_redirect_if_required>
|
||||
instead of calling this directly.
|
||||
|
||||
=item C<redirect_to_urlbase>
|
||||
|
||||
Redirects from the current URL to one prefixed by the urlbase parameter.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<CGI|CGI>, L<CGI::Cookie|CGI::Cookie>
|
||||
@@ -1,444 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Gervase Markham <gerv@gerv.net>
|
||||
# Albert Ting <altlst@sonic.net>
|
||||
# A. Karl Kornel <karl@kornel.name>
|
||||
|
||||
use strict;
|
||||
|
||||
# This module represents a chart.
|
||||
#
|
||||
# Note that it is perfectly legal for the 'lines' member variable of this
|
||||
# class (which is an array of Bugzilla::Series objects) to have empty members
|
||||
# in it. If this is true, the 'labels' array will also have empty members at
|
||||
# the same points.
|
||||
package Bugzilla::Chart;
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Series;
|
||||
|
||||
use Date::Format;
|
||||
use Date::Parse;
|
||||
use List::Util qw(max);
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
|
||||
# Create a ref to an empty hash and bless it
|
||||
my $self = {};
|
||||
bless($self, $class);
|
||||
|
||||
if ($#_ == 0) {
|
||||
# Construct from a CGI object.
|
||||
$self->init($_[0]);
|
||||
}
|
||||
else {
|
||||
die("CGI object not passed in - invalid number of args \($#_\)($_)");
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub init {
|
||||
my $self = shift;
|
||||
my $cgi = shift;
|
||||
|
||||
# The data structure is a list of lists (lines) of Series objects.
|
||||
# There is a separate list for the labels.
|
||||
#
|
||||
# The URL encoding is:
|
||||
# line0=67&line0=73&line1=81&line2=67...
|
||||
# &label0=B+/+R+/+CONFIRMED&label1=...
|
||||
# &select0=1&select3=1...
|
||||
# &cumulate=1&datefrom=2002-02-03&dateto=2002-04-04&ctype=html...
|
||||
# >=1&labelgt=Grand+Total
|
||||
foreach my $param ($cgi->param()) {
|
||||
# Store all the lines
|
||||
if ($param =~ /^line(\d+)$/) {
|
||||
foreach my $series_id ($cgi->param($param)) {
|
||||
detaint_natural($series_id)
|
||||
|| ThrowCodeError("invalid_series_id");
|
||||
my $series = new Bugzilla::Series($series_id);
|
||||
push(@{$self->{'lines'}[$1]}, $series) if $series;
|
||||
}
|
||||
}
|
||||
|
||||
# Store all the labels
|
||||
if ($param =~ /^label(\d+)$/) {
|
||||
$self->{'labels'}[$1] = $cgi->param($param);
|
||||
}
|
||||
}
|
||||
|
||||
# Store the miscellaneous metadata
|
||||
$self->{'cumulate'} = $cgi->param('cumulate') ? 1 : 0;
|
||||
$self->{'gt'} = $cgi->param('gt') ? 1 : 0;
|
||||
$self->{'labelgt'} = $cgi->param('labelgt');
|
||||
$self->{'datefrom'} = $cgi->param('datefrom');
|
||||
$self->{'dateto'} = $cgi->param('dateto');
|
||||
|
||||
# If we are cumulating, a grand total makes no sense
|
||||
$self->{'gt'} = 0 if $self->{'cumulate'};
|
||||
|
||||
# Make sure the dates are ones we are able to interpret
|
||||
foreach my $date ('datefrom', 'dateto') {
|
||||
if ($self->{$date}) {
|
||||
$self->{$date} = str2time($self->{$date})
|
||||
|| ThrowUserError("illegal_date", { date => $self->{$date}});
|
||||
}
|
||||
}
|
||||
|
||||
# datefrom can't be after dateto
|
||||
if ($self->{'datefrom'} && $self->{'dateto'} &&
|
||||
$self->{'datefrom'} > $self->{'dateto'})
|
||||
{
|
||||
ThrowUserError('misarranged_dates', { 'datefrom' => scalar $cgi->param('datefrom'),
|
||||
'dateto' => scalar $cgi->param('dateto') });
|
||||
}
|
||||
}
|
||||
|
||||
# Alter Chart so that the selected series are added to it.
|
||||
sub add {
|
||||
my $self = shift;
|
||||
my @series_ids = @_;
|
||||
|
||||
# Get the current size of the series; required for adding Grand Total later
|
||||
my $current_size = scalar($self->getSeriesIDs());
|
||||
|
||||
# Count the number of added series
|
||||
my $added = 0;
|
||||
# Create new Series and push them on to the list of lines.
|
||||
# Note that new lines have no label; the display template is responsible
|
||||
# for inventing something sensible.
|
||||
foreach my $series_id (@series_ids) {
|
||||
my $series = new Bugzilla::Series($series_id);
|
||||
if ($series) {
|
||||
push(@{$self->{'lines'}}, [$series]);
|
||||
push(@{$self->{'labels'}}, "");
|
||||
$added++;
|
||||
}
|
||||
}
|
||||
|
||||
# If we are going from < 2 to >= 2 series, add the Grand Total line.
|
||||
if (!$self->{'gt'}) {
|
||||
if ($current_size < 2 &&
|
||||
$current_size + $added >= 2)
|
||||
{
|
||||
$self->{'gt'} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Alter Chart so that the selections are removed from it.
|
||||
sub remove {
|
||||
my $self = shift;
|
||||
my @line_ids = @_;
|
||||
|
||||
foreach my $line_id (@line_ids) {
|
||||
if ($line_id == 65536) {
|
||||
# Magic value - delete Grand Total.
|
||||
$self->{'gt'} = 0;
|
||||
}
|
||||
else {
|
||||
delete($self->{'lines'}->[$line_id]);
|
||||
delete($self->{'labels'}->[$line_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Alter Chart so that the selections are summed.
|
||||
sub sum {
|
||||
my $self = shift;
|
||||
my @line_ids = @_;
|
||||
|
||||
# We can't add the Grand Total to things.
|
||||
@line_ids = grep(!/^65536$/, @line_ids);
|
||||
|
||||
# We can't add less than two things.
|
||||
return if scalar(@line_ids) < 2;
|
||||
|
||||
my @series;
|
||||
my $label = "";
|
||||
my $biggestlength = 0;
|
||||
|
||||
# We rescue the Series objects of all the series involved in the sum.
|
||||
foreach my $line_id (@line_ids) {
|
||||
my @line = @{$self->{'lines'}->[$line_id]};
|
||||
|
||||
foreach my $series (@line) {
|
||||
push(@series, $series);
|
||||
}
|
||||
|
||||
# We keep the label that labels the line with the most series.
|
||||
if (scalar(@line) > $biggestlength) {
|
||||
$biggestlength = scalar(@line);
|
||||
$label = $self->{'labels'}->[$line_id];
|
||||
}
|
||||
}
|
||||
|
||||
$self->remove(@line_ids);
|
||||
|
||||
push(@{$self->{'lines'}}, \@series);
|
||||
push(@{$self->{'labels'}}, $label);
|
||||
}
|
||||
|
||||
sub data {
|
||||
my $self = shift;
|
||||
$self->{'_data'} ||= $self->readData();
|
||||
return $self->{'_data'};
|
||||
}
|
||||
|
||||
# Convert the Chart's data into a plottable form in $self->{'_data'}.
|
||||
sub readData {
|
||||
my $self = shift;
|
||||
my @data;
|
||||
my @maxvals;
|
||||
|
||||
# Note: you get a bad image if getSeriesIDs returns nothing
|
||||
# We need to handle errors better.
|
||||
my $series_ids = join(",", $self->getSeriesIDs());
|
||||
|
||||
return [] unless $series_ids;
|
||||
|
||||
# Work out the date boundaries for our data.
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# The date used is the one given if it's in a sensible range; otherwise,
|
||||
# it's the earliest or latest date in the database as appropriate.
|
||||
my $datefrom = $dbh->selectrow_array("SELECT MIN(series_date) " .
|
||||
"FROM series_data " .
|
||||
"WHERE series_id IN ($series_ids)");
|
||||
$datefrom = str2time($datefrom);
|
||||
|
||||
if ($self->{'datefrom'} && $self->{'datefrom'} > $datefrom) {
|
||||
$datefrom = $self->{'datefrom'};
|
||||
}
|
||||
|
||||
my $dateto = $dbh->selectrow_array("SELECT MAX(series_date) " .
|
||||
"FROM series_data " .
|
||||
"WHERE series_id IN ($series_ids)");
|
||||
$dateto = str2time($dateto);
|
||||
|
||||
if ($self->{'dateto'} && $self->{'dateto'} < $dateto) {
|
||||
$dateto = $self->{'dateto'};
|
||||
}
|
||||
|
||||
# Convert UNIX times back to a date format usable for SQL queries.
|
||||
my $sql_from = time2str('%Y-%m-%d', $datefrom);
|
||||
my $sql_to = time2str('%Y-%m-%d', $dateto);
|
||||
|
||||
# Prepare the query which retrieves the data for each series
|
||||
my $query = "SELECT " . $dbh->sql_to_days('series_date') . " - " .
|
||||
$dbh->sql_to_days('?') . ", series_value " .
|
||||
"FROM series_data " .
|
||||
"WHERE series_id = ? " .
|
||||
"AND series_date >= ?";
|
||||
if ($dateto) {
|
||||
$query .= " AND series_date <= ?";
|
||||
}
|
||||
|
||||
my $sth = $dbh->prepare($query);
|
||||
|
||||
my $gt_index = $self->{'gt'} ? scalar(@{$self->{'lines'}}) : undef;
|
||||
my $line_index = 0;
|
||||
|
||||
$maxvals[$gt_index] = 0 if $gt_index;
|
||||
|
||||
my @datediff_total;
|
||||
|
||||
foreach my $line (@{$self->{'lines'}}) {
|
||||
# Even if we end up with no data, we need an empty arrayref to prevent
|
||||
# errors in the PNG-generating code
|
||||
$data[$line_index] = [];
|
||||
$maxvals[$line_index] = 0;
|
||||
|
||||
foreach my $series (@$line) {
|
||||
|
||||
# Get the data for this series and add it on
|
||||
if ($dateto) {
|
||||
$sth->execute($sql_from, $series->{'series_id'}, $sql_from, $sql_to);
|
||||
}
|
||||
else {
|
||||
$sth->execute($sql_from, $series->{'series_id'}, $sql_from);
|
||||
}
|
||||
my $points = $sth->fetchall_arrayref();
|
||||
|
||||
foreach my $point (@$points) {
|
||||
my ($datediff, $value) = @$point;
|
||||
$data[$line_index][$datediff] ||= 0;
|
||||
$data[$line_index][$datediff] += $value;
|
||||
if ($data[$line_index][$datediff] > $maxvals[$line_index]) {
|
||||
$maxvals[$line_index] = $data[$line_index][$datediff];
|
||||
}
|
||||
|
||||
$datediff_total[$datediff] += $value;
|
||||
|
||||
# Add to the grand total, if we are doing that
|
||||
if ($gt_index) {
|
||||
$data[$gt_index][$datediff] ||= 0;
|
||||
$data[$gt_index][$datediff] += $value;
|
||||
if ($data[$gt_index][$datediff] > $maxvals[$gt_index]) {
|
||||
$maxvals[$gt_index] = $data[$gt_index][$datediff];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# We are done with the series making up this line, go to the next one
|
||||
$line_index++;
|
||||
}
|
||||
|
||||
# calculate maximum y value
|
||||
if ($self->{'cumulate'}) {
|
||||
# Make sure we do not try to take the max of an array with undef values
|
||||
my @processed_datediff;
|
||||
while (@datediff_total) {
|
||||
my $datediff = shift @datediff_total;
|
||||
push @processed_datediff, $datediff if defined($datediff);
|
||||
}
|
||||
$self->{'y_max_value'} = max(@processed_datediff);
|
||||
}
|
||||
else {
|
||||
$self->{'y_max_value'} = max(@maxvals);
|
||||
}
|
||||
$self->{'y_max_value'} |= 1; # For log()
|
||||
|
||||
# Align the max y value:
|
||||
# For one- or two-digit numbers, increase y_max_value until divisible by 8
|
||||
# For larger numbers, see the comments below to figure out what's going on
|
||||
if ($self->{'y_max_value'} < 100) {
|
||||
do {
|
||||
++$self->{'y_max_value'};
|
||||
} while ($self->{'y_max_value'} % 8 != 0);
|
||||
}
|
||||
else {
|
||||
# First, get the # of digits in the y_max_value
|
||||
my $num_digits = 1+int(log($self->{'y_max_value'})/log(10));
|
||||
|
||||
# We want to zero out all but the top 2 digits
|
||||
my $mask_length = $num_digits - 2;
|
||||
$self->{'y_max_value'} /= 10**$mask_length;
|
||||
$self->{'y_max_value'} = int($self->{'y_max_value'});
|
||||
$self->{'y_max_value'} *= 10**$mask_length;
|
||||
|
||||
# Add 10^$mask_length to the max value
|
||||
# Continue to increase until it's divisible by 8 * 10^($mask_length-1)
|
||||
# (Throwing in the -1 keeps at least the smallest digit at zero)
|
||||
do {
|
||||
$self->{'y_max_value'} += 10**$mask_length;
|
||||
} while ($self->{'y_max_value'} % (8*(10**($mask_length-1))) != 0);
|
||||
}
|
||||
|
||||
|
||||
# Add the x-axis labels into the data structure
|
||||
my $date_progression = generateDateProgression($datefrom, $dateto);
|
||||
unshift(@data, $date_progression);
|
||||
|
||||
if ($self->{'gt'}) {
|
||||
# Add Grand Total to label list
|
||||
push(@{$self->{'labels'}}, $self->{'labelgt'});
|
||||
|
||||
$data[$gt_index] ||= [];
|
||||
}
|
||||
|
||||
return \@data;
|
||||
}
|
||||
|
||||
# Flatten the data structure into a list of series_ids
|
||||
sub getSeriesIDs {
|
||||
my $self = shift;
|
||||
my @series_ids;
|
||||
|
||||
foreach my $line (@{$self->{'lines'}}) {
|
||||
foreach my $series (@$line) {
|
||||
push(@series_ids, $series->{'series_id'});
|
||||
}
|
||||
}
|
||||
|
||||
return @series_ids;
|
||||
}
|
||||
|
||||
# Class method to get the data necessary to populate the "select series"
|
||||
# widgets on various pages.
|
||||
sub getVisibleSeries {
|
||||
my %cats;
|
||||
|
||||
my $grouplist = Bugzilla->user->groups_as_string;
|
||||
|
||||
# Get all visible series
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $serieses = $dbh->selectall_arrayref("SELECT cc1.name, cc2.name, " .
|
||||
"series.name, series.series_id " .
|
||||
"FROM series " .
|
||||
"INNER JOIN series_categories AS cc1 " .
|
||||
" ON series.category = cc1.id " .
|
||||
"INNER JOIN series_categories AS cc2 " .
|
||||
" ON series.subcategory = cc2.id " .
|
||||
"LEFT JOIN category_group_map AS cgm " .
|
||||
" ON series.category = cgm.category_id " .
|
||||
" AND cgm.group_id NOT IN($grouplist) " .
|
||||
"WHERE creator = ? OR (is_public = 1 AND cgm.category_id IS NULL) " .
|
||||
$dbh->sql_group_by('series.series_id', 'cc1.name, cc2.name, ' .
|
||||
'series.name'),
|
||||
undef, Bugzilla->user->id);
|
||||
foreach my $series (@$serieses) {
|
||||
my ($cat, $subcat, $name, $series_id) = @$series;
|
||||
$cats{$cat}{$subcat}{$name} = $series_id;
|
||||
}
|
||||
|
||||
return \%cats;
|
||||
}
|
||||
|
||||
sub generateDateProgression {
|
||||
my ($datefrom, $dateto) = @_;
|
||||
my @progression;
|
||||
|
||||
$dateto = $dateto || time();
|
||||
my $oneday = 60 * 60 * 24;
|
||||
|
||||
# When the from and to dates are converted by str2time(), you end up with
|
||||
# a time figure representing midnight at the beginning of that day. We
|
||||
# adjust the times by 1/3 and 2/3 of a day respectively to prevent
|
||||
# edge conditions in time2str().
|
||||
$datefrom += $oneday / 3;
|
||||
$dateto += (2 * $oneday) / 3;
|
||||
|
||||
while ($datefrom < $dateto) {
|
||||
push (@progression, time2str("%Y-%m-%d", $datefrom));
|
||||
$datefrom += $oneday;
|
||||
}
|
||||
|
||||
return \@progression;
|
||||
}
|
||||
|
||||
sub dump {
|
||||
my $self = shift;
|
||||
|
||||
# Make sure we've read in our data
|
||||
my $data = $self->data;
|
||||
|
||||
require Data::Dumper;
|
||||
print "<pre>Bugzilla::Chart object:\n";
|
||||
print html_quote(Data::Dumper::Dumper($self));
|
||||
print "</pre>";
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,221 +0,0 @@
|
||||
# -*- 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): Tiago R. Mello <timello@async.com.br>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::Classification;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Field;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Product;
|
||||
|
||||
use base qw(Bugzilla::Field::ChoiceInterface Bugzilla::Object);
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
use constant DB_TABLE => 'classifications';
|
||||
use constant LIST_ORDER => 'sortkey, name';
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
id
|
||||
name
|
||||
description
|
||||
sortkey
|
||||
);
|
||||
|
||||
use constant UPDATE_COLUMNS => qw(
|
||||
name
|
||||
description
|
||||
sortkey
|
||||
);
|
||||
|
||||
use constant VALIDATORS => {
|
||||
name => \&_check_name,
|
||||
description => \&_check_description,
|
||||
sortkey => \&_check_sortkey,
|
||||
};
|
||||
|
||||
###############################
|
||||
#### Constructors #####
|
||||
###############################
|
||||
sub remove_from_db {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
ThrowUserError("classification_not_deletable") if ($self->id == 1);
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
# Reclassify products to the default classification, if needed.
|
||||
$dbh->do("UPDATE products SET classification_id = 1
|
||||
WHERE classification_id = ?", undef, $self->id);
|
||||
|
||||
$self->SUPER::remove_from_db();
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Validators ####
|
||||
###############################
|
||||
|
||||
sub _check_name {
|
||||
my ($invocant, $name) = @_;
|
||||
|
||||
$name = trim($name);
|
||||
$name || ThrowUserError('classification_not_specified');
|
||||
|
||||
if (length($name) > MAX_CLASSIFICATION_SIZE) {
|
||||
ThrowUserError('classification_name_too_long', {'name' => $name});
|
||||
}
|
||||
|
||||
my $classification = new Bugzilla::Classification({name => $name});
|
||||
if ($classification && (!ref $invocant || $classification->id != $invocant->id)) {
|
||||
ThrowUserError("classification_already_exists", { name => $classification->name });
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub _check_description {
|
||||
my ($invocant, $description) = @_;
|
||||
|
||||
$description = trim($description || '');
|
||||
return $description;
|
||||
}
|
||||
|
||||
sub _check_sortkey {
|
||||
my ($invocant, $sortkey) = @_;
|
||||
|
||||
$sortkey ||= 0;
|
||||
my $stored_sortkey = $sortkey;
|
||||
if (!detaint_natural($sortkey) || $sortkey > MAX_SMALLINT) {
|
||||
ThrowUserError('classification_invalid_sortkey', { 'sortkey' => $stored_sortkey });
|
||||
}
|
||||
return $sortkey;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Implement Bugzilla::Field::Choice #
|
||||
#####################################
|
||||
|
||||
use constant FIELD_NAME => 'classification';
|
||||
use constant is_default => 0;
|
||||
use constant is_active => 1;
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
sub set_name { $_[0]->set('name', $_[1]); }
|
||||
sub set_description { $_[0]->set('description', $_[1]); }
|
||||
sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
|
||||
|
||||
sub product_count {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!defined $self->{'product_count'}) {
|
||||
$self->{'product_count'} = $dbh->selectrow_array(q{
|
||||
SELECT COUNT(*) FROM products
|
||||
WHERE classification_id = ?}, undef, $self->id) || 0;
|
||||
}
|
||||
return $self->{'product_count'};
|
||||
}
|
||||
|
||||
sub products {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!$self->{'products'}) {
|
||||
my $product_ids = $dbh->selectcol_arrayref(q{
|
||||
SELECT id FROM products
|
||||
WHERE classification_id = ?
|
||||
ORDER BY name}, undef, $self->id);
|
||||
|
||||
$self->{'products'} = Bugzilla::Product->new_from_list($product_ids);
|
||||
}
|
||||
return $self->{'products'};
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ####
|
||||
###############################
|
||||
|
||||
sub description { return $_[0]->{'description'}; }
|
||||
sub sortkey { return $_[0]->{'sortkey'}; }
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Classification - Bugzilla classification class.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Classification;
|
||||
|
||||
my $classification = new Bugzilla::Classification(1);
|
||||
my $classification = new Bugzilla::Classification({name => 'Acme'});
|
||||
|
||||
my $id = $classification->id;
|
||||
my $name = $classification->name;
|
||||
my $description = $classification->description;
|
||||
my $sortkey = $classification->sortkey;
|
||||
my $product_count = $classification->product_count;
|
||||
my $products = $classification->products;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Classification.pm represents a classification object. It is an
|
||||
implementation of L<Bugzilla::Object>, and thus provides all methods
|
||||
that L<Bugzilla::Object> provides.
|
||||
|
||||
The methods that are specific to C<Bugzilla::Classification> are listed
|
||||
below.
|
||||
|
||||
A Classification is a higher-level grouping of Products.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<product_count()>
|
||||
|
||||
Description: Returns the total number of products that belong to
|
||||
the classification.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: Integer - The total of products inside the classification.
|
||||
|
||||
=item C<products>
|
||||
|
||||
Description: Returns all products of the classification.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: A reference to an array of Bugzilla::Product objects.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
@@ -1,316 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is James Robson.
|
||||
# Portions created by James Robson are Copyright (c) 2009 James Robson.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Contributor(s): James Robson <arbingersys@gmail.com>
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::Comment;
|
||||
|
||||
use base qw(Bugzilla::Object);
|
||||
|
||||
use Bugzilla::Attachment;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
comment_id
|
||||
bug_id
|
||||
who
|
||||
bug_when
|
||||
work_time
|
||||
thetext
|
||||
isprivate
|
||||
already_wrapped
|
||||
type
|
||||
extra_data
|
||||
);
|
||||
|
||||
use constant UPDATE_COLUMNS => qw(
|
||||
type
|
||||
extra_data
|
||||
);
|
||||
|
||||
use constant DB_TABLE => 'longdescs';
|
||||
use constant ID_FIELD => 'comment_id';
|
||||
use constant LIST_ORDER => 'bug_when';
|
||||
|
||||
use constant VALIDATORS => {
|
||||
extra_data => \&_check_extra_data,
|
||||
type => \&_check_type,
|
||||
};
|
||||
|
||||
use constant VALIDATOR_DEPENDENCIES => {
|
||||
extra_data => ['type'],
|
||||
};
|
||||
|
||||
#########################
|
||||
# Database Manipulation #
|
||||
#########################
|
||||
|
||||
sub update {
|
||||
my $self = shift;
|
||||
my $changes = $self->SUPER::update(@_);
|
||||
$self->bug->_sync_fulltext();
|
||||
return $changes;
|
||||
}
|
||||
|
||||
# Speeds up displays of comment lists by loading all ->author objects
|
||||
# at once for a whole list.
|
||||
sub preload {
|
||||
my ($class, $comments) = @_;
|
||||
my %user_ids = map { $_->{who} => 1 } @$comments;
|
||||
my $users = Bugzilla::User->new_from_list([keys %user_ids]);
|
||||
my %user_map = map { $_->id => $_ } @$users;
|
||||
foreach my $comment (@$comments) {
|
||||
$comment->{author} = $user_map{$comment->{who}};
|
||||
}
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ######
|
||||
###############################
|
||||
|
||||
sub already_wrapped { return $_[0]->{'already_wrapped'}; }
|
||||
sub body { return $_[0]->{'thetext'}; }
|
||||
sub bug_id { return $_[0]->{'bug_id'}; }
|
||||
sub creation_ts { return $_[0]->{'bug_when'}; }
|
||||
sub is_private { return $_[0]->{'isprivate'}; }
|
||||
sub work_time { return $_[0]->{'work_time'}; }
|
||||
sub type { return $_[0]->{'type'}; }
|
||||
sub extra_data { return $_[0]->{'extra_data'} }
|
||||
|
||||
sub bug {
|
||||
my $self = shift;
|
||||
require Bugzilla::Bug;
|
||||
$self->{bug} ||= new Bugzilla::Bug($self->bug_id);
|
||||
return $self->{bug};
|
||||
}
|
||||
|
||||
sub is_about_attachment {
|
||||
my ($self) = @_;
|
||||
return 1 if ($self->type == CMT_ATTACHMENT_CREATED
|
||||
or $self->type == CMT_ATTACHMENT_UPDATED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub attachment {
|
||||
my ($self) = @_;
|
||||
return undef if not $self->is_about_attachment;
|
||||
$self->{attachment} ||= new Bugzilla::Attachment($self->extra_data);
|
||||
return $self->{attachment};
|
||||
}
|
||||
|
||||
sub author {
|
||||
my $self = shift;
|
||||
$self->{'author'} ||= new Bugzilla::User($self->{'who'});
|
||||
return $self->{'author'};
|
||||
}
|
||||
|
||||
sub body_full {
|
||||
my ($self, $params) = @_;
|
||||
$params ||= {};
|
||||
my $template = Bugzilla->template_inner;
|
||||
my $body;
|
||||
if ($self->type) {
|
||||
$template->process("bug/format_comment.txt.tmpl",
|
||||
{ comment => $self, %$params }, \$body)
|
||||
|| ThrowTemplateError($template->error());
|
||||
$body =~ s/^X//;
|
||||
}
|
||||
else {
|
||||
$body = $self->body;
|
||||
}
|
||||
if ($params->{wrap} and !$self->already_wrapped) {
|
||||
$body = wrap_comment($body);
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
|
||||
############
|
||||
# Mutators #
|
||||
############
|
||||
|
||||
sub set_extra_data { $_[0]->set('extra_data', $_[1]); }
|
||||
|
||||
sub set_type {
|
||||
my ($self, $type) = @_;
|
||||
$self->set('type', $type);
|
||||
}
|
||||
|
||||
##############
|
||||
# Validators #
|
||||
##############
|
||||
|
||||
sub _check_extra_data {
|
||||
my ($invocant, $extra_data, undef, $params) = @_;
|
||||
my $type = blessed($invocant) ? $invocant->type : $params->{type};
|
||||
|
||||
if ($type == CMT_NORMAL) {
|
||||
if (defined $extra_data) {
|
||||
ThrowCodeError('comment_extra_data_not_allowed',
|
||||
{ type => $type, extra_data => $extra_data });
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!defined $extra_data) {
|
||||
ThrowCodeError('comment_extra_data_required', { type => $type });
|
||||
}
|
||||
elsif ($type == CMT_ATTACHMENT_CREATED
|
||||
or $type == CMT_ATTACHMENT_UPDATED)
|
||||
{
|
||||
my $attachment = Bugzilla::Attachment->check({
|
||||
id => $extra_data });
|
||||
$extra_data = $attachment->id;
|
||||
}
|
||||
else {
|
||||
my $original = $extra_data;
|
||||
detaint_natural($extra_data)
|
||||
or ThrowCodeError('comment_extra_data_not_numeric',
|
||||
{ type => $type, extra_data => $original });
|
||||
}
|
||||
}
|
||||
|
||||
return $extra_data;
|
||||
}
|
||||
|
||||
sub _check_type {
|
||||
my ($invocant, $type) = @_;
|
||||
$type ||= CMT_NORMAL;
|
||||
my $original = $type;
|
||||
detaint_natural($type)
|
||||
or ThrowCodeError('comment_type_invalid', { type => $original });
|
||||
return $type;
|
||||
}
|
||||
|
||||
sub count {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->{'count'} if defined $self->{'count'};
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
($self->{'count'}) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*)
|
||||
FROM longdescs
|
||||
WHERE bug_id = ?
|
||||
AND bug_when <= ?",
|
||||
undef, $self->bug_id, $self->creation_ts);
|
||||
|
||||
return --$self->{'count'};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Comment - A Comment for a given bug
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Comment;
|
||||
|
||||
my $comment = Bugzilla::Comment->new($comment_id);
|
||||
my $comments = Bugzilla::Comment->new_from_list($comment_ids);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Bugzilla::Comment represents a comment attached to a bug.
|
||||
|
||||
This implements all standard C<Bugzilla::Object> methods. See
|
||||
L<Bugzilla::Object> for more details.
|
||||
|
||||
=head2 Accessors
|
||||
|
||||
=over
|
||||
|
||||
=item C<bug_id>
|
||||
|
||||
C<int> The ID of the bug to which the comment belongs.
|
||||
|
||||
=item C<creation_ts>
|
||||
|
||||
C<string> The comment creation timestamp.
|
||||
|
||||
=item C<body>
|
||||
|
||||
C<string> The body without any special additional text.
|
||||
|
||||
=item C<work_time>
|
||||
|
||||
C<string> Time spent as related to this comment.
|
||||
|
||||
=item C<is_private>
|
||||
|
||||
C<boolean> Comment is marked as private
|
||||
|
||||
=item C<already_wrapped>
|
||||
|
||||
If this comment is stored in the database word-wrapped, this will be C<1>.
|
||||
C<0> otherwise.
|
||||
|
||||
=item C<author>
|
||||
|
||||
L<Bugzilla::User> who created the comment.
|
||||
|
||||
=item C<count>
|
||||
|
||||
C<int> The position this comment is located in the full list of comments for a bug starting from 0.
|
||||
|
||||
=item C<body_full>
|
||||
|
||||
=over
|
||||
|
||||
=item B<Description>
|
||||
|
||||
C<string> Body of the comment, including any special text (such as
|
||||
"this bug was marked as a duplicate of...").
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
||||
=item C<is_bugmail>
|
||||
|
||||
C<boolean>. C<1> if this comment should be formatted specifically for
|
||||
bugmail.
|
||||
|
||||
=item C<wrap>
|
||||
|
||||
C<boolean>. C<1> if the comment should be returned word-wrapped.
|
||||
|
||||
=back
|
||||
|
||||
=item B<Returns>
|
||||
|
||||
A string, the full text of the comment as it would be displayed to an end-user.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
@@ -1,657 +0,0 @@
|
||||
# -*- 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): Tiago R. Mello <timello@async.com.br>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
# Akamai Technologies <bugzilla-dev@akamai.com>
|
||||
|
||||
package Bugzilla::Component;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Field::ChoiceInterface Bugzilla::Object);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::FlagType;
|
||||
use Bugzilla::Series;
|
||||
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
use constant DB_TABLE => 'components';
|
||||
# This is mostly for the editfields.cgi case where ->get_all is called.
|
||||
use constant LIST_ORDER => 'product_id, name';
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
id
|
||||
name
|
||||
product_id
|
||||
initialowner
|
||||
initialqacontact
|
||||
description
|
||||
);
|
||||
|
||||
use constant UPDATE_COLUMNS => qw(
|
||||
name
|
||||
initialowner
|
||||
initialqacontact
|
||||
description
|
||||
);
|
||||
|
||||
use constant REQUIRED_FIELD_MAP => {
|
||||
product_id => 'product',
|
||||
};
|
||||
|
||||
use constant VALIDATORS => {
|
||||
create_series => \&Bugzilla::Object::check_boolean,
|
||||
product => \&_check_product,
|
||||
initialowner => \&_check_initialowner,
|
||||
initialqacontact => \&_check_initialqacontact,
|
||||
description => \&_check_description,
|
||||
initial_cc => \&_check_cc_list,
|
||||
name => \&_check_name,
|
||||
};
|
||||
|
||||
use constant VALIDATOR_DEPENDENCIES => {
|
||||
name => ['product'],
|
||||
};
|
||||
|
||||
###############################
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $param = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $product;
|
||||
if (ref $param and !defined $param->{id}) {
|
||||
$product = $param->{product};
|
||||
my $name = $param->{name};
|
||||
if (!defined $product) {
|
||||
ThrowCodeError('bad_arg',
|
||||
{argument => 'product',
|
||||
function => "${class}::new"});
|
||||
}
|
||||
if (!defined $name) {
|
||||
ThrowCodeError('bad_arg',
|
||||
{argument => 'name',
|
||||
function => "${class}::new"});
|
||||
}
|
||||
|
||||
my $condition = 'product_id = ? AND name = ?';
|
||||
my @values = ($product->id, $name);
|
||||
$param = { condition => $condition, values => \@values };
|
||||
}
|
||||
|
||||
unshift @_, $param;
|
||||
my $component = $class->SUPER::new(@_);
|
||||
# Add the product object as attribute only if the component exists.
|
||||
$component->{product} = $product if ($component && $product);
|
||||
return $component;
|
||||
}
|
||||
|
||||
sub create {
|
||||
my $class = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
$class->check_required_create_fields(@_);
|
||||
my $params = $class->run_create_validators(@_);
|
||||
my $cc_list = delete $params->{initial_cc};
|
||||
my $create_series = delete $params->{create_series};
|
||||
my $product = delete $params->{product};
|
||||
$params->{product_id} = $product->id;
|
||||
|
||||
my $component = $class->insert_create_data($params);
|
||||
$component->{product} = $product;
|
||||
|
||||
# We still have to fill the component_cc table.
|
||||
$component->_update_cc_list($cc_list) if $cc_list;
|
||||
|
||||
# Create series for the new component.
|
||||
$component->_create_series() if $create_series;
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
return $component;
|
||||
}
|
||||
|
||||
sub update {
|
||||
my $self = shift;
|
||||
my $changes = $self->SUPER::update(@_);
|
||||
|
||||
# Update the component_cc table if necessary.
|
||||
if (defined $self->{cc_ids}) {
|
||||
my $diff = $self->_update_cc_list($self->{cc_ids});
|
||||
$changes->{cc_list} = $diff if defined $diff;
|
||||
}
|
||||
return $changes;
|
||||
}
|
||||
|
||||
sub remove_from_db {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$self->_check_if_controller(); # From ChoiceInterface
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
if ($self->bug_count) {
|
||||
if (Bugzilla->params->{'allowbugdeletion'}) {
|
||||
require Bugzilla::Bug;
|
||||
foreach my $bug_id (@{$self->bug_ids}) {
|
||||
# Note: We allow admins to delete bugs even if they can't
|
||||
# see them, as long as they can see the product.
|
||||
my $bug = new Bugzilla::Bug($bug_id);
|
||||
$bug->remove_from_db();
|
||||
}
|
||||
} else {
|
||||
ThrowUserError('component_has_bugs', {nb => $self->bug_count});
|
||||
}
|
||||
}
|
||||
|
||||
$dbh->do('DELETE FROM flaginclusions WHERE component_id = ?',
|
||||
undef, $self->id);
|
||||
$dbh->do('DELETE FROM flagexclusions WHERE component_id = ?',
|
||||
undef, $self->id);
|
||||
$dbh->do('DELETE FROM component_cc WHERE component_id = ?',
|
||||
undef, $self->id);
|
||||
$dbh->do('DELETE FROM components WHERE id = ?', undef, $self->id);
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
}
|
||||
|
||||
################################
|
||||
# Validators
|
||||
################################
|
||||
|
||||
sub _check_name {
|
||||
my ($invocant, $name, undef, $params) = @_;
|
||||
my $product = blessed($invocant) ? $invocant->product : $params->{product};
|
||||
|
||||
$name = trim($name);
|
||||
$name || ThrowUserError('component_blank_name');
|
||||
|
||||
if (length($name) > MAX_COMPONENT_SIZE) {
|
||||
ThrowUserError('component_name_too_long', {'name' => $name});
|
||||
}
|
||||
|
||||
my $component = new Bugzilla::Component({product => $product, name => $name});
|
||||
if ($component && (!ref $invocant || $component->id != $invocant->id)) {
|
||||
ThrowUserError('component_already_exists', { name => $component->name,
|
||||
product => $product });
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub _check_description {
|
||||
my ($invocant, $description) = @_;
|
||||
|
||||
$description = trim($description);
|
||||
$description || ThrowUserError('component_blank_description');
|
||||
return $description;
|
||||
}
|
||||
|
||||
sub _check_initialowner {
|
||||
my ($invocant, $owner) = @_;
|
||||
|
||||
$owner || ThrowUserError('component_need_initialowner');
|
||||
my $owner_id = Bugzilla::User->check($owner)->id;
|
||||
return $owner_id;
|
||||
}
|
||||
|
||||
sub _check_initialqacontact {
|
||||
my ($invocant, $qa_contact) = @_;
|
||||
|
||||
my $qa_contact_id;
|
||||
if (Bugzilla->params->{'useqacontact'}) {
|
||||
$qa_contact_id = Bugzilla::User->check($qa_contact)->id if $qa_contact;
|
||||
}
|
||||
elsif (ref $invocant) {
|
||||
$qa_contact_id = $invocant->{initialqacontact};
|
||||
}
|
||||
return $qa_contact_id;
|
||||
}
|
||||
|
||||
sub _check_product {
|
||||
my ($invocant, $product) = @_;
|
||||
$product || ThrowCodeError('param_required',
|
||||
{ function => "$invocant->create", param => 'product' });
|
||||
return Bugzilla->user->check_can_admin_product($product->name);
|
||||
}
|
||||
|
||||
sub _check_cc_list {
|
||||
my ($invocant, $cc_list) = @_;
|
||||
|
||||
my %cc_ids;
|
||||
foreach my $cc (@$cc_list) {
|
||||
my $id = login_to_id($cc, THROW_ERROR);
|
||||
$cc_ids{$id} = 1;
|
||||
}
|
||||
return [keys %cc_ids];
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
sub _update_cc_list {
|
||||
my ($self, $cc_list) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $old_cc_list =
|
||||
$dbh->selectcol_arrayref('SELECT user_id FROM component_cc
|
||||
WHERE component_id = ?', undef, $self->id);
|
||||
|
||||
my ($removed, $added) = diff_arrays($old_cc_list, $cc_list);
|
||||
my $diff;
|
||||
if (scalar @$removed || scalar @$added) {
|
||||
$diff = [join(', ', @$removed), join(', ', @$added)];
|
||||
}
|
||||
|
||||
$dbh->do('DELETE FROM component_cc WHERE component_id = ?', undef, $self->id);
|
||||
|
||||
my $sth = $dbh->prepare('INSERT INTO component_cc
|
||||
(user_id, component_id) VALUES (?, ?)');
|
||||
$sth->execute($_, $self->id) foreach (@$cc_list);
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
sub _create_series {
|
||||
my $self = shift;
|
||||
|
||||
# Insert default charting queries for this product.
|
||||
# If they aren't using charting, this won't do any harm.
|
||||
my $prodcomp = "&product=" . url_quote($self->product->name) .
|
||||
"&component=" . url_quote($self->name);
|
||||
|
||||
my $open_query = 'field0-0-0=resolution&type0-0-0=notregexp&value0-0-0=.' .
|
||||
$prodcomp;
|
||||
my $nonopen_query = 'field0-0-0=resolution&type0-0-0=regexp&value0-0-0=.' .
|
||||
$prodcomp;
|
||||
|
||||
my @series = ([get_text('series_all_open'), $open_query],
|
||||
[get_text('series_all_closed'), $nonopen_query]);
|
||||
|
||||
foreach my $sdata (@series) {
|
||||
my $series = new Bugzilla::Series(undef, $self->product->name,
|
||||
$self->name, $sdata->[0],
|
||||
Bugzilla->user->id, 1, $sdata->[1], 1);
|
||||
$series->writeToDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
sub set_name { $_[0]->set('name', $_[1]); }
|
||||
sub set_description { $_[0]->set('description', $_[1]); }
|
||||
sub set_default_assignee {
|
||||
my ($self, $owner) = @_;
|
||||
|
||||
$self->set('initialowner', $owner);
|
||||
# Reset the default owner object.
|
||||
delete $self->{default_assignee};
|
||||
}
|
||||
sub set_default_qa_contact {
|
||||
my ($self, $qa_contact) = @_;
|
||||
|
||||
$self->set('initialqacontact', $qa_contact);
|
||||
# Reset the default QA contact object.
|
||||
delete $self->{default_qa_contact};
|
||||
}
|
||||
sub set_cc_list {
|
||||
my ($self, $cc_list) = @_;
|
||||
|
||||
$self->{cc_ids} = $self->_check_cc_list($cc_list);
|
||||
# Reset the list of CC user objects.
|
||||
delete $self->{initial_cc};
|
||||
}
|
||||
|
||||
sub bug_count {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!defined $self->{'bug_count'}) {
|
||||
$self->{'bug_count'} = $dbh->selectrow_array(q{
|
||||
SELECT COUNT(*) FROM bugs
|
||||
WHERE component_id = ?}, undef, $self->id) || 0;
|
||||
}
|
||||
return $self->{'bug_count'};
|
||||
}
|
||||
|
||||
sub bug_ids {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!defined $self->{'bugs_ids'}) {
|
||||
$self->{'bugs_ids'} = $dbh->selectcol_arrayref(q{
|
||||
SELECT bug_id FROM bugs
|
||||
WHERE component_id = ?}, undef, $self->id);
|
||||
}
|
||||
return $self->{'bugs_ids'};
|
||||
}
|
||||
|
||||
sub default_assignee {
|
||||
my $self = shift;
|
||||
|
||||
if (!defined $self->{'default_assignee'}) {
|
||||
$self->{'default_assignee'} =
|
||||
new Bugzilla::User($self->{'initialowner'});
|
||||
}
|
||||
return $self->{'default_assignee'};
|
||||
}
|
||||
|
||||
sub default_qa_contact {
|
||||
my $self = shift;
|
||||
|
||||
if (!defined $self->{'default_qa_contact'}) {
|
||||
$self->{'default_qa_contact'} =
|
||||
new Bugzilla::User($self->{'initialqacontact'});
|
||||
}
|
||||
return $self->{'default_qa_contact'};
|
||||
}
|
||||
|
||||
sub flag_types {
|
||||
my $self = shift;
|
||||
|
||||
if (!defined $self->{'flag_types'}) {
|
||||
my $flagtypes = Bugzilla::FlagType::match({ product_id => $self->product_id,
|
||||
component_id => $self->id });
|
||||
|
||||
$self->{'flag_types'} = {};
|
||||
$self->{'flag_types'}->{'bug'} =
|
||||
[grep { $_->target_type eq 'bug' } @$flagtypes];
|
||||
$self->{'flag_types'}->{'attachment'} =
|
||||
[grep { $_->target_type eq 'attachment' } @$flagtypes];
|
||||
}
|
||||
return $self->{'flag_types'};
|
||||
}
|
||||
|
||||
sub initial_cc {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!defined $self->{'initial_cc'}) {
|
||||
# If set_cc_list() has been called but data are not yet written
|
||||
# into the DB, we want the new values defined by it.
|
||||
my $cc_ids = $self->{cc_ids}
|
||||
|| $dbh->selectcol_arrayref('SELECT user_id FROM component_cc
|
||||
WHERE component_id = ?',
|
||||
undef, $self->id);
|
||||
|
||||
$self->{'initial_cc'} = Bugzilla::User->new_from_list($cc_ids);
|
||||
}
|
||||
return $self->{'initial_cc'};
|
||||
}
|
||||
|
||||
sub product {
|
||||
my $self = shift;
|
||||
if (!defined $self->{'product'}) {
|
||||
require Bugzilla::Product; # We cannot |use| it.
|
||||
$self->{'product'} = new Bugzilla::Product($self->product_id);
|
||||
}
|
||||
return $self->{'product'};
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ####
|
||||
###############################
|
||||
|
||||
sub description { return $_[0]->{'description'}; }
|
||||
sub product_id { return $_[0]->{'product_id'}; }
|
||||
|
||||
##############################################
|
||||
# Implement Bugzilla::Field::ChoiceInterface #
|
||||
##############################################
|
||||
|
||||
use constant FIELD_NAME => 'component';
|
||||
use constant is_default => 0;
|
||||
use constant is_active => 1;
|
||||
|
||||
sub is_set_on_bug {
|
||||
my ($self, $bug) = @_;
|
||||
# We treat it like a hash always, so that we don't have to check if it's
|
||||
# a hash or an object.
|
||||
return 0 if !defined $bug->{component_id};
|
||||
$bug->{component_id} == $self->id ? 1 : 0;
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Subroutines ####
|
||||
###############################
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Component - Bugzilla product component class.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Component;
|
||||
|
||||
my $component = new Bugzilla::Component($comp_id);
|
||||
my $component = new Bugzilla::Component({ product => $product, name => $name });
|
||||
|
||||
my $bug_count = $component->bug_count();
|
||||
my $bug_ids = $component->bug_ids();
|
||||
my $id = $component->id;
|
||||
my $name = $component->name;
|
||||
my $description = $component->description;
|
||||
my $product_id = $component->product_id;
|
||||
my $default_assignee = $component->default_assignee;
|
||||
my $default_qa_contact = $component->default_qa_contact;
|
||||
my $initial_cc = $component->initial_cc;
|
||||
my $product = $component->product;
|
||||
my $bug_flag_types = $component->flag_types->{'bug'};
|
||||
my $attach_flag_types = $component->flag_types->{'attachment'};
|
||||
|
||||
my $component = Bugzilla::Component->check({ product => $product, name => $name });
|
||||
|
||||
my $component =
|
||||
Bugzilla::Component->create({ name => $name,
|
||||
product => $product,
|
||||
initialowner => $user_login1,
|
||||
initialqacontact => $user_login2,
|
||||
description => $description});
|
||||
|
||||
$component->set_name($new_name);
|
||||
$component->set_description($new_description);
|
||||
$component->set_default_assignee($new_login_name);
|
||||
$component->set_default_qa_contact($new_login_name);
|
||||
$component->set_cc_list(\@new_login_names);
|
||||
$component->update();
|
||||
|
||||
$component->remove_from_db;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Component.pm represents a Product Component object.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<new($param)>
|
||||
|
||||
Description: The constructor is used to load an existing component
|
||||
by passing a component ID or a hash with the product
|
||||
object the component belongs to and the component name.
|
||||
|
||||
Params: $param - If you pass an integer, the integer is the
|
||||
component ID from the database that we want to
|
||||
read in. If you pass in a hash with the 'name'
|
||||
and 'product' keys, then the value of the name
|
||||
key is the name of a component being in the given
|
||||
product.
|
||||
|
||||
Returns: A Bugzilla::Component object.
|
||||
|
||||
=item C<bug_count()>
|
||||
|
||||
Description: Returns the total of bugs that belong to the component.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: Integer with the number of bugs.
|
||||
|
||||
=item C<bugs_ids()>
|
||||
|
||||
Description: Returns all bug IDs that belong to the component.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: A reference to an array of bug IDs.
|
||||
|
||||
=item C<default_assignee()>
|
||||
|
||||
Description: Returns a user object that represents the default assignee for
|
||||
the component.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: A Bugzilla::User object.
|
||||
|
||||
=item C<default_qa_contact()>
|
||||
|
||||
Description: Returns a user object that represents the default QA contact for
|
||||
the component.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: A Bugzilla::User object.
|
||||
|
||||
=item C<initial_cc>
|
||||
|
||||
Description: Returns a list of user objects representing users being
|
||||
in the initial CC list.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: An arrayref of L<Bugzilla::User> objects.
|
||||
|
||||
=item C<flag_types()>
|
||||
|
||||
Description: Returns all bug and attachment flagtypes available for
|
||||
the component.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: Two references to an array of flagtype objects.
|
||||
|
||||
=item C<product()>
|
||||
|
||||
Description: Returns the product the component belongs to.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: A Bugzilla::Product object.
|
||||
|
||||
=item C<set_name($new_name)>
|
||||
|
||||
Description: Changes the name of the component.
|
||||
|
||||
Params: $new_name - new name of the component (string). This name
|
||||
must be unique within the product.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<set_description($new_desc)>
|
||||
|
||||
Description: Changes the description of the component.
|
||||
|
||||
Params: $new_desc - new description of the component (string).
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<set_default_assignee($new_assignee)>
|
||||
|
||||
Description: Changes the default assignee of the component.
|
||||
|
||||
Params: $new_owner - login name of the new default assignee of
|
||||
the component (string). This user account
|
||||
must already exist.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<set_default_qa_contact($new_qa_contact)>
|
||||
|
||||
Description: Changes the default QA contact of the component.
|
||||
|
||||
Params: $new_qa_contact - login name of the new QA contact of
|
||||
the component (string). This user
|
||||
account must already exist.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<set_cc_list(\@cc_list)>
|
||||
|
||||
Description: Changes the list of users being in the CC list by default.
|
||||
|
||||
Params: \@cc_list - list of login names (string). All the user
|
||||
accounts must already exist.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<update()>
|
||||
|
||||
Description: Write changes made to the component into the DB.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: A hashref with changes made to the component object.
|
||||
|
||||
=item C<remove_from_db()>
|
||||
|
||||
Description: Deletes the current component from the DB. The object itself
|
||||
is not destroyed.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=back
|
||||
|
||||
=head1 CLASS METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<create(\%params)>
|
||||
|
||||
Description: Create a new component for the given product.
|
||||
|
||||
Params: The hashref must have the following keys:
|
||||
name - name of the new component (string). This name
|
||||
must be unique within the product.
|
||||
product - a Bugzilla::Product object to which
|
||||
the Component is being added.
|
||||
description - description of the new component (string).
|
||||
initialowner - login name of the default assignee (string).
|
||||
The following keys are optional:
|
||||
initiaqacontact - login name of the default QA contact (string),
|
||||
or an empty string to clear it.
|
||||
initial_cc - an arrayref of login names to add to the
|
||||
CC list by default.
|
||||
|
||||
Returns: A Bugzilla::Component object.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
@@ -1,408 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jake <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
|
||||
package Bugzilla::Config;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Exporter);
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Hook;
|
||||
use Bugzilla::Install::Filesystem qw(fix_file_permissions);
|
||||
use Data::Dumper;
|
||||
use File::Temp;
|
||||
|
||||
# Don't export localvars by default - people should have to explicitly
|
||||
# ask for it, as a (probably futile) attempt to stop code using it
|
||||
# when it shouldn't
|
||||
%Bugzilla::Config::EXPORT_TAGS =
|
||||
(
|
||||
admin => [qw(update_params SetParam write_params)],
|
||||
);
|
||||
Exporter::export_ok_tags('admin');
|
||||
|
||||
use vars qw(@param_list);
|
||||
|
||||
# INITIALISATION CODE
|
||||
# Perl throws a warning if we use bz_locations() directly after do.
|
||||
our %params;
|
||||
# Load in the param definitions
|
||||
sub _load_params {
|
||||
my $panels = param_panels();
|
||||
my %hook_panels;
|
||||
foreach my $panel (keys %$panels) {
|
||||
my $module = $panels->{$panel};
|
||||
eval("require $module") || die $@;
|
||||
my @new_param_list = $module->get_param_list();
|
||||
$hook_panels{lc($panel)} = { params => \@new_param_list };
|
||||
foreach my $item (@new_param_list) {
|
||||
$params{$item->{'name'}} = $item;
|
||||
}
|
||||
push(@param_list, @new_param_list);
|
||||
}
|
||||
# This hook is also called in editparams.cgi. This call here is required
|
||||
# to make SetParam work.
|
||||
Bugzilla::Hook::process('config_modify_panels',
|
||||
{ panels => \%hook_panels });
|
||||
}
|
||||
# END INIT CODE
|
||||
|
||||
# Subroutines go here
|
||||
|
||||
sub param_panels {
|
||||
my $param_panels = {};
|
||||
my $libpath = bz_locations()->{'libpath'};
|
||||
foreach my $item ((glob "$libpath/Bugzilla/Config/*.pm")) {
|
||||
$item =~ m#/([^/]+)\.pm$#;
|
||||
my $module = $1;
|
||||
$param_panels->{$module} = "Bugzilla::Config::$module" unless $module eq 'Common';
|
||||
}
|
||||
# Now check for any hooked params
|
||||
Bugzilla::Hook::process('config_add_panels',
|
||||
{ panel_modules => $param_panels });
|
||||
return $param_panels;
|
||||
}
|
||||
|
||||
sub SetParam {
|
||||
my ($name, $value) = @_;
|
||||
|
||||
_load_params unless %params;
|
||||
die "Unknown param $name" unless (exists $params{$name});
|
||||
|
||||
my $entry = $params{$name};
|
||||
|
||||
# sanity check the value
|
||||
|
||||
# XXX - This runs the checks. Which would be good, except that
|
||||
# check_shadowdb creates the database as a side effect, and so the
|
||||
# checker fails the second time around...
|
||||
if ($name ne 'shadowdb' && exists $entry->{'checker'}) {
|
||||
my $err = $entry->{'checker'}->($value, $entry);
|
||||
die "Param $name is not valid: $err" unless $err eq '';
|
||||
}
|
||||
|
||||
Bugzilla->params->{$name} = $value;
|
||||
}
|
||||
|
||||
sub update_params {
|
||||
my ($params) = @_;
|
||||
my $answer = Bugzilla->installation_answers;
|
||||
|
||||
my $param = read_param_file();
|
||||
|
||||
# If we didn't return any param values, then this is a new installation.
|
||||
my $new_install = !(keys %$param);
|
||||
|
||||
# --- UPDATE OLD PARAMS ---
|
||||
|
||||
# Old Bugzilla versions stored the version number in the params file
|
||||
# We don't want it, so get rid of it
|
||||
delete $param->{'version'};
|
||||
|
||||
# Change from usebrowserinfo to defaultplatform/defaultopsys combo
|
||||
if (exists $param->{'usebrowserinfo'}) {
|
||||
if (!$param->{'usebrowserinfo'}) {
|
||||
if (!exists $param->{'defaultplatform'}) {
|
||||
$param->{'defaultplatform'} = 'Other';
|
||||
}
|
||||
if (!exists $param->{'defaultopsys'}) {
|
||||
$param->{'defaultopsys'} = 'Other';
|
||||
}
|
||||
}
|
||||
delete $param->{'usebrowserinfo'};
|
||||
}
|
||||
|
||||
# Change from a boolean for quips to multi-state
|
||||
if (exists $param->{'usequip'} && !exists $param->{'enablequips'}) {
|
||||
$param->{'enablequips'} = $param->{'usequip'} ? 'on' : 'off';
|
||||
delete $param->{'usequip'};
|
||||
}
|
||||
|
||||
# Change from old product groups to controls for group_control_map
|
||||
# 2002-10-14 bug 147275 bugreport@peshkin.net
|
||||
if (exists $param->{'usebuggroups'} &&
|
||||
!exists $param->{'makeproductgroups'})
|
||||
{
|
||||
$param->{'makeproductgroups'} = $param->{'usebuggroups'};
|
||||
}
|
||||
|
||||
# Modularise auth code
|
||||
if (exists $param->{'useLDAP'} && !exists $param->{'loginmethod'}) {
|
||||
$param->{'loginmethod'} = $param->{'useLDAP'} ? "LDAP" : "DB";
|
||||
}
|
||||
|
||||
# set verify method to whatever loginmethod was
|
||||
if (exists $param->{'loginmethod'}
|
||||
&& !exists $param->{'user_verify_class'})
|
||||
{
|
||||
$param->{'user_verify_class'} = $param->{'loginmethod'};
|
||||
delete $param->{'loginmethod'};
|
||||
}
|
||||
|
||||
# Remove quip-display control from parameters
|
||||
# and give it to users via User Settings (Bug 41972)
|
||||
if ( exists $param->{'enablequips'}
|
||||
&& !exists $param->{'quip_list_entry_control'})
|
||||
{
|
||||
my $new_value;
|
||||
($param->{'enablequips'} eq 'on') && do {$new_value = 'open';};
|
||||
($param->{'enablequips'} eq 'approved') && do {$new_value = 'moderated';};
|
||||
($param->{'enablequips'} eq 'frozen') && do {$new_value = 'closed';};
|
||||
($param->{'enablequips'} eq 'off') && do {$new_value = 'closed';};
|
||||
$param->{'quip_list_entry_control'} = $new_value;
|
||||
delete $param->{'enablequips'};
|
||||
}
|
||||
|
||||
# Old mail_delivery_method choices contained no uppercase characters
|
||||
if (exists $param->{'mail_delivery_method'}
|
||||
&& $param->{'mail_delivery_method'} !~ /[A-Z]/) {
|
||||
my $method = $param->{'mail_delivery_method'};
|
||||
my %translation = (
|
||||
'sendmail' => 'Sendmail',
|
||||
'smtp' => 'SMTP',
|
||||
'qmail' => 'Qmail',
|
||||
'testfile' => 'Test',
|
||||
'none' => 'None');
|
||||
$param->{'mail_delivery_method'} = $translation{$method};
|
||||
}
|
||||
|
||||
# Convert the old "ssl" parameter to the new "ssl_redirect" parameter.
|
||||
# Both "authenticated sessions" and "always" turn on "ssl_redirect"
|
||||
# when upgrading.
|
||||
if (exists $param->{'ssl'} and $param->{'ssl'} ne 'never') {
|
||||
$param->{'ssl_redirect'} = 1;
|
||||
}
|
||||
|
||||
# --- DEFAULTS FOR NEW PARAMS ---
|
||||
|
||||
_load_params unless %params;
|
||||
foreach my $item (@param_list) {
|
||||
my $name = $item->{'name'};
|
||||
unless (exists $param->{$name}) {
|
||||
print "New parameter: $name\n" unless $new_install;
|
||||
if (exists $answer->{$name}) {
|
||||
$param->{$name} = $answer->{$name};
|
||||
}
|
||||
else {
|
||||
$param->{$name} = $item->{'default'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$param->{'utf8'} = 1 if $new_install;
|
||||
|
||||
# --- REMOVE OLD PARAMS ---
|
||||
|
||||
my %oldparams;
|
||||
# Remove any old params
|
||||
foreach my $item (keys %$param) {
|
||||
if (!grep($_ eq $item, map ($_->{'name'}, @param_list))) {
|
||||
$oldparams{$item} = $param->{$item};
|
||||
delete $param->{$item};
|
||||
}
|
||||
}
|
||||
|
||||
# Write any old parameters to old-params.txt
|
||||
my $datadir = bz_locations()->{'datadir'};
|
||||
my $old_param_file = "$datadir/old-params.txt";
|
||||
if (scalar(keys %oldparams)) {
|
||||
my $op_file = new IO::File($old_param_file, '>>', 0600)
|
||||
|| die "Couldn't create $old_param_file: $!";
|
||||
|
||||
print "The following parameters are no longer used in Bugzilla,",
|
||||
" and so have been\nmoved from your parameters file into",
|
||||
" $old_param_file:\n";
|
||||
|
||||
local $Data::Dumper::Terse = 1;
|
||||
local $Data::Dumper::Indent = 0;
|
||||
|
||||
my $comma = "";
|
||||
foreach my $item (keys %oldparams) {
|
||||
print $op_file "\n\n$item:\n" . Data::Dumper->Dump([$oldparams{$item}]) . "\n";
|
||||
print "${comma}$item";
|
||||
$comma = ", ";
|
||||
}
|
||||
print "\n";
|
||||
$op_file->close;
|
||||
}
|
||||
|
||||
if (ON_WINDOWS && !-e SENDMAIL_EXE
|
||||
&& $param->{'mail_delivery_method'} eq 'Sendmail')
|
||||
{
|
||||
my $smtp = $answer->{'SMTP_SERVER'};
|
||||
if (!$smtp) {
|
||||
print "\nBugzilla requires an SMTP server to function on",
|
||||
" Windows.\nPlease enter your SMTP server's hostname: ";
|
||||
$smtp = <STDIN>;
|
||||
chomp $smtp;
|
||||
if ($smtp) {
|
||||
$param->{'smtpserver'} = $smtp;
|
||||
}
|
||||
else {
|
||||
print "\nWarning: No SMTP Server provided, defaulting to",
|
||||
" localhost\n";
|
||||
}
|
||||
}
|
||||
|
||||
$param->{'mail_delivery_method'} = 'SMTP';
|
||||
}
|
||||
|
||||
write_params($param);
|
||||
|
||||
# Return deleted params and values so that checksetup.pl has a chance
|
||||
# to convert old params to new data.
|
||||
return %oldparams;
|
||||
}
|
||||
|
||||
sub write_params {
|
||||
my ($param_data) = @_;
|
||||
$param_data ||= Bugzilla->params;
|
||||
|
||||
my $datadir = bz_locations()->{'datadir'};
|
||||
my $param_file = "$datadir/params";
|
||||
|
||||
local $Data::Dumper::Sortkeys = 1;
|
||||
|
||||
my ($fh, $tmpname) = File::Temp::tempfile('params.XXXXX',
|
||||
DIR => $datadir );
|
||||
|
||||
print $fh (Data::Dumper->Dump([$param_data], ['*param']))
|
||||
|| die "Can't write param file: $!";
|
||||
|
||||
close $fh;
|
||||
|
||||
rename $tmpname, $param_file
|
||||
or die "Can't rename $tmpname to $param_file: $!";
|
||||
|
||||
fix_file_permissions($param_file);
|
||||
|
||||
# And now we have to reset the params cache so that Bugzilla will re-read
|
||||
# them.
|
||||
delete Bugzilla->request_cache->{params};
|
||||
}
|
||||
|
||||
sub read_param_file {
|
||||
my %params;
|
||||
my $datadir = bz_locations()->{'datadir'};
|
||||
if (-e "$datadir/params") {
|
||||
# Note that checksetup.pl sets file permissions on '$datadir/params'
|
||||
|
||||
# Using Safe mode is _not_ a guarantee of safety if someone does
|
||||
# manage to write to the file. However, it won't hurt...
|
||||
# See bug 165144 for not needing to eval this at all
|
||||
my $s = new Safe;
|
||||
|
||||
$s->rdo("$datadir/params");
|
||||
die "Error reading $datadir/params: $!" if $!;
|
||||
die "Error evaluating $datadir/params: $@" if $@;
|
||||
|
||||
# Now read the param back out from the sandbox
|
||||
%params = %{$s->varglob('param')};
|
||||
}
|
||||
elsif ($ENV{'SERVER_SOFTWARE'}) {
|
||||
# We're in a CGI, but the params file doesn't exist. We can't
|
||||
# Template Toolkit, or even install_string, since checksetup
|
||||
# might not have thrown an error. Bugzilla::CGI->new
|
||||
# hasn't even been called yet, so we manually use CGI::Carp here
|
||||
# so that the user sees the error.
|
||||
require CGI::Carp;
|
||||
CGI::Carp->import('fatalsToBrowser');
|
||||
die "The $datadir/params file does not exist."
|
||||
. ' You probably need to run checksetup.pl.',
|
||||
}
|
||||
return \%params;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Config - Configuration parameters for Bugzilla
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
# Administration functions
|
||||
use Bugzilla::Config qw(:admin);
|
||||
|
||||
update_params();
|
||||
SetParam($param, $value);
|
||||
write_params();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This package contains ways to access Bugzilla configuration parameters.
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=head2 Parameters
|
||||
|
||||
Parameters can be set, retrieved, and updated.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<SetParam($name, $value)>
|
||||
|
||||
Sets the param named $name to $value. Values are checked using the checker
|
||||
function for the given param if one exists.
|
||||
|
||||
=item C<update_params()>
|
||||
|
||||
Updates the parameters, by transitioning old params to new formats, setting
|
||||
defaults for new params, and removing obsolete ones. Used by F<checksetup.pl>
|
||||
in the process of an installation or upgrade.
|
||||
|
||||
Prints out information about what it's doing, if it makes any changes.
|
||||
|
||||
May prompt the user for input, if certain required parameters are not
|
||||
specified.
|
||||
|
||||
=item C<write_params($params)>
|
||||
|
||||
Description: Writes the parameters to disk.
|
||||
|
||||
Params: C<$params> (optional) - A hashref to write to the disk
|
||||
instead of C<Bugzilla->params>. Used only by
|
||||
C<update_params>.
|
||||
|
||||
Returns: nothing
|
||||
|
||||
=item C<read_param_file()>
|
||||
|
||||
Description: Most callers should never need this. This is used
|
||||
by C<Bugzilla->params> to directly read C<$datadir/params>
|
||||
and load it into memory. Use C<Bugzilla->params> instead.
|
||||
|
||||
Params: none
|
||||
|
||||
Returns: A hashref containing the current params in C<$datadir/params>.
|
||||
|
||||
=back
|
||||
@@ -1,63 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::Admin;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 200;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'allowbugdeletion',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'allowemailchange',
|
||||
type => 'b',
|
||||
default => 1
|
||||
},
|
||||
|
||||
{
|
||||
name => 'allowuserdeletion',
|
||||
type => 'b',
|
||||
default => 0
|
||||
});
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,68 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Config::Advanced;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 1700;
|
||||
|
||||
use constant get_param_list => (
|
||||
{
|
||||
name => 'cookiedomain',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'inbound_proxies',
|
||||
type => 't',
|
||||
default => '',
|
||||
checker => \&check_ip
|
||||
},
|
||||
|
||||
{
|
||||
name => 'proxy_url',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'strict_transport_security',
|
||||
type => 's',
|
||||
choices => ['off', 'this_domain_only', 'include_subdomains'],
|
||||
default => 'off',
|
||||
checker => \&check_multi
|
||||
},
|
||||
);
|
||||
|
||||
1;
|
||||
@@ -1,91 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::Attachment;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 400;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'allow_attachment_display',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'attachment_base',
|
||||
type => 't',
|
||||
default => '',
|
||||
checker => \&check_urlbase
|
||||
},
|
||||
|
||||
{
|
||||
name => 'allow_attachment_deletion',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
{
|
||||
name => 'allow_attach_url',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'maxattachmentsize',
|
||||
type => 't',
|
||||
default => '1000',
|
||||
checker => \&check_maxattachmentsize
|
||||
},
|
||||
|
||||
# The maximum size (in bytes) for patches and non-patch attachments.
|
||||
# The default limit is 1000KB, which is 24KB less than mysql's default
|
||||
# maximum packet size (which determines how much data can be sent in a
|
||||
# single mysql packet and thus how much data can be inserted into the
|
||||
# database) to provide breathing space for the data in other fields of
|
||||
# the attachment record as well as any mysql packet overhead (I don't
|
||||
# know of any, but I suspect there may be some.)
|
||||
|
||||
{
|
||||
name => 'maxlocalattachment',
|
||||
type => 't',
|
||||
default => '0',
|
||||
checker => \&check_numeric
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,128 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::Auth;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 300;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'auth_env_id',
|
||||
type => 't',
|
||||
default => '',
|
||||
},
|
||||
|
||||
{
|
||||
name => 'auth_env_email',
|
||||
type => 't',
|
||||
default => '',
|
||||
},
|
||||
|
||||
{
|
||||
name => 'auth_env_realname',
|
||||
type => 't',
|
||||
default => '',
|
||||
},
|
||||
|
||||
# XXX in the future:
|
||||
#
|
||||
# user_verify_class and user_info_class should have choices gathered from
|
||||
# whatever sits in their respective directories
|
||||
#
|
||||
# rather than comma-separated lists, these two should eventually become
|
||||
# arrays, but that requires alterations to editparams first
|
||||
|
||||
{
|
||||
name => 'user_info_class',
|
||||
type => 's',
|
||||
choices => [ 'CGI', 'Env', 'Env,CGI' ],
|
||||
default => 'CGI',
|
||||
checker => \&check_multi
|
||||
},
|
||||
|
||||
{
|
||||
name => 'user_verify_class',
|
||||
type => 'o',
|
||||
choices => [ 'DB', 'RADIUS', 'LDAP' ],
|
||||
default => 'DB',
|
||||
checker => \&check_user_verify_class
|
||||
},
|
||||
|
||||
{
|
||||
name => 'rememberlogin',
|
||||
type => 's',
|
||||
choices => ['on', 'defaulton', 'defaultoff', 'off'],
|
||||
default => 'on',
|
||||
checker => \&check_multi
|
||||
},
|
||||
|
||||
{
|
||||
name => 'requirelogin',
|
||||
type => 'b',
|
||||
default => '0'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'emailregexp',
|
||||
type => 't',
|
||||
default => q:^[\\w\\.\\+\\-=]+@[\\w\\.\\-]+\\.[\\w\\-]+$:,
|
||||
checker => \&check_regexp
|
||||
},
|
||||
|
||||
{
|
||||
name => 'emailregexpdesc',
|
||||
type => 'l',
|
||||
default => 'A legal address must contain exactly one \'@\', and at least ' .
|
||||
'one \'.\' after the @.'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'emailsuffix',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'createemailregexp',
|
||||
type => 't',
|
||||
default => q:.*:,
|
||||
checker => \&check_regexp
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,103 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::BugChange;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
use Bugzilla::Status;
|
||||
|
||||
our $sortkey = 500;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
|
||||
# Hardcoded bug statuses which existed before Bugzilla 3.1.
|
||||
my @closed_bug_statuses = ('RESOLVED', 'VERIFIED', 'CLOSED');
|
||||
|
||||
# If we are upgrading from 3.0 or older, bug statuses are not customisable
|
||||
# and bug_status.is_open is not yet defined (hence the eval), so we use
|
||||
# the bug statuses above as they are still hardcoded.
|
||||
eval {
|
||||
my @current_closed_states = map {$_->name} closed_bug_statuses();
|
||||
# If no closed state was found, use the default list above.
|
||||
@closed_bug_statuses = @current_closed_states if scalar(@current_closed_states);
|
||||
};
|
||||
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'duplicate_or_move_bug_status',
|
||||
type => 's',
|
||||
choices => \@closed_bug_statuses,
|
||||
default => $closed_bug_statuses[0],
|
||||
checker => \&check_bug_status
|
||||
},
|
||||
|
||||
{
|
||||
name => 'letsubmitterchoosepriority',
|
||||
type => 'b',
|
||||
default => 1
|
||||
},
|
||||
|
||||
{
|
||||
name => 'letsubmitterchoosemilestone',
|
||||
type => 'b',
|
||||
default => 1
|
||||
},
|
||||
|
||||
{
|
||||
name => 'musthavemilestoneonaccept',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'commentonchange_resolution',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'commentonduplicate',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'noresolveonopenblockers',
|
||||
type => 'b',
|
||||
default => 0,
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,120 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::BugFields;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
use Bugzilla::Field;
|
||||
|
||||
our $sortkey = 600;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
|
||||
my @legal_priorities = @{get_legal_field_values('priority')};
|
||||
my @legal_severities = @{get_legal_field_values('bug_severity')};
|
||||
my @legal_platforms = @{get_legal_field_values('rep_platform')};
|
||||
my @legal_OS = @{get_legal_field_values('op_sys')};
|
||||
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'useclassification',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'usetargetmilestone',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'useqacontact',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'usestatuswhiteboard',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'usebugaliases',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'use_see_also',
|
||||
type => 'b',
|
||||
default => 1
|
||||
},
|
||||
|
||||
{
|
||||
name => 'defaultpriority',
|
||||
type => 's',
|
||||
choices => \@legal_priorities,
|
||||
default => $legal_priorities[-1],
|
||||
checker => \&check_priority
|
||||
},
|
||||
|
||||
{
|
||||
name => 'defaultseverity',
|
||||
type => 's',
|
||||
choices => \@legal_severities,
|
||||
default => $legal_severities[-1],
|
||||
checker => \&check_severity
|
||||
},
|
||||
|
||||
{
|
||||
name => 'defaultplatform',
|
||||
type => 's',
|
||||
choices => ['', @legal_platforms],
|
||||
default => '',
|
||||
checker => \&check_platform
|
||||
},
|
||||
|
||||
{
|
||||
name => 'defaultopsys',
|
||||
type => 's',
|
||||
choices => ['', @legal_OS],
|
||||
default => '',
|
||||
checker => \&check_opsys
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,470 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
# Marc Schumann <wurblzap@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::Common;
|
||||
|
||||
use strict;
|
||||
|
||||
use Email::Address;
|
||||
use Socket;
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Field;
|
||||
use Bugzilla::Group;
|
||||
use Bugzilla::Status;
|
||||
|
||||
use base qw(Exporter);
|
||||
@Bugzilla::Config::Common::EXPORT =
|
||||
qw(check_multi check_numeric check_regexp check_url check_group
|
||||
check_sslbase check_priority check_severity check_platform
|
||||
check_opsys check_shadowdb check_urlbase check_webdotbase
|
||||
check_user_verify_class check_ip
|
||||
check_mail_delivery_method check_notification check_utf8
|
||||
check_bug_status check_smtp_auth check_theschwartz_available
|
||||
check_maxattachmentsize check_email
|
||||
);
|
||||
|
||||
# Checking functions for the various values
|
||||
|
||||
sub check_multi {
|
||||
my ($value, $param) = (@_);
|
||||
|
||||
if ($param->{'type'} eq "s") {
|
||||
unless (scalar(grep {$_ eq $value} (@{$param->{'choices'}}))) {
|
||||
return "Invalid choice '$value' for single-select list param '$param->{'name'}'";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
elsif ($param->{'type'} eq 'm' || $param->{'type'} eq 'o') {
|
||||
foreach my $chkParam (split(',', $value)) {
|
||||
unless (scalar(grep {$_ eq $chkParam} (@{$param->{'choices'}}))) {
|
||||
return "Invalid choice '$chkParam' for multi-select list param '$param->{'name'}'";
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return "Invalid param type '$param->{'type'}' for check_multi(); " .
|
||||
"contact your Bugzilla administrator";
|
||||
}
|
||||
}
|
||||
|
||||
sub check_numeric {
|
||||
my ($value) = (@_);
|
||||
if ($value !~ /^[0-9]+$/) {
|
||||
return "must be a numeric value";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_regexp {
|
||||
my ($value) = (@_);
|
||||
eval { qr/$value/ };
|
||||
return $@;
|
||||
}
|
||||
|
||||
sub check_email {
|
||||
my ($value) = @_;
|
||||
if ($value !~ $Email::Address::mailbox) {
|
||||
return "must be a valid email address.";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_sslbase {
|
||||
my $url = shift;
|
||||
if ($url ne '') {
|
||||
if ($url !~ m#^https://([^/]+).*/$#) {
|
||||
return "must be a legal URL, that starts with https and ends with a slash.";
|
||||
}
|
||||
my $host = $1;
|
||||
# Fall back to port 443 if for some reason getservbyname() fails.
|
||||
my $port = getservbyname('https', 'tcp') || 443;
|
||||
if ($host =~ /^(.+):(\d+)$/) {
|
||||
$host = $1;
|
||||
$port = $2;
|
||||
}
|
||||
local *SOCK;
|
||||
my $proto = getprotobyname('tcp');
|
||||
socket(SOCK, PF_INET, SOCK_STREAM, $proto);
|
||||
my $iaddr = inet_aton($host) || return "The host $host cannot be resolved";
|
||||
my $sin = sockaddr_in($port, $iaddr);
|
||||
if (!connect(SOCK, $sin)) {
|
||||
return "Failed to connect to $host:$port; unable to enable SSL";
|
||||
}
|
||||
close(SOCK);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_ip {
|
||||
my $inbound_proxies = shift;
|
||||
my @proxies = split(/[\s,]+/, $inbound_proxies);
|
||||
foreach my $proxy (@proxies) {
|
||||
validate_ip($proxy) || return "$proxy is not a valid IPv4 or IPv6 address";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_utf8 {
|
||||
my $utf8 = shift;
|
||||
# You cannot turn off the UTF-8 parameter if you've already converted
|
||||
# your tables to utf-8.
|
||||
my $dbh = Bugzilla->dbh;
|
||||
if ($dbh->isa('Bugzilla::DB::Mysql') && $dbh->bz_db_is_utf8 && !$utf8) {
|
||||
return "You cannot disable UTF-8 support, because your MySQL database"
|
||||
. " is encoded in UTF-8";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_priority {
|
||||
my ($value) = (@_);
|
||||
my $legal_priorities = get_legal_field_values('priority');
|
||||
if (!grep($_ eq $value, @$legal_priorities)) {
|
||||
return "Must be a legal priority value: one of " .
|
||||
join(", ", @$legal_priorities);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_severity {
|
||||
my ($value) = (@_);
|
||||
my $legal_severities = get_legal_field_values('bug_severity');
|
||||
if (!grep($_ eq $value, @$legal_severities)) {
|
||||
return "Must be a legal severity value: one of " .
|
||||
join(", ", @$legal_severities);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_platform {
|
||||
my ($value) = (@_);
|
||||
my $legal_platforms = get_legal_field_values('rep_platform');
|
||||
if (!grep($_ eq $value, '', @$legal_platforms)) {
|
||||
return "Must be empty or a legal platform value: one of " .
|
||||
join(", ", @$legal_platforms);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_opsys {
|
||||
my ($value) = (@_);
|
||||
my $legal_OS = get_legal_field_values('op_sys');
|
||||
if (!grep($_ eq $value, '', @$legal_OS)) {
|
||||
return "Must be empty or a legal operating system value: one of " .
|
||||
join(", ", @$legal_OS);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_bug_status {
|
||||
my $bug_status = shift;
|
||||
my @closed_bug_statuses = map {$_->name} closed_bug_statuses();
|
||||
if (!grep($_ eq $bug_status, @closed_bug_statuses)) {
|
||||
return "Must be a valid closed status: one of " . join(', ', @closed_bug_statuses);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_group {
|
||||
my $group_name = shift;
|
||||
return "" unless $group_name;
|
||||
my $group = new Bugzilla::Group({'name' => $group_name});
|
||||
unless (defined $group) {
|
||||
return "Must be an existing group name";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_shadowdb {
|
||||
my ($value) = (@_);
|
||||
$value = trim($value);
|
||||
if ($value eq "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!Bugzilla->params->{'shadowdbhost'}) {
|
||||
return "You need to specify a host when using a shadow database";
|
||||
}
|
||||
|
||||
# Can't test existence of this because ConnectToDatabase uses the param,
|
||||
# but we can't set this before testing....
|
||||
# This can really only be fixed after we can use the DBI more openly
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_urlbase {
|
||||
my ($url) = (@_);
|
||||
if ($url && $url !~ m:^http.*/$:) {
|
||||
return "must be a legal URL, that starts with http and ends with a slash.";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_url {
|
||||
my ($url) = (@_);
|
||||
return '' if $url eq ''; # Allow empty URLs
|
||||
if ($url !~ m:/$:) {
|
||||
return 'must be a legal URL, absolute or relative, ending with a slash.';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
sub check_webdotbase {
|
||||
my ($value) = (@_);
|
||||
$value = trim($value);
|
||||
if ($value eq "") {
|
||||
return "";
|
||||
}
|
||||
if($value !~ /^https?:/) {
|
||||
if(! -x $value) {
|
||||
return "The file path \"$value\" is not a valid executable. Please specify the complete file path to 'dot' if you intend to generate graphs locally.";
|
||||
}
|
||||
# Check .htaccess allows access to generated images
|
||||
my $webdotdir = bz_locations()->{'webdotdir'};
|
||||
if(-e "$webdotdir/.htaccess") {
|
||||
open HTACCESS, "<", "$webdotdir/.htaccess";
|
||||
if(! grep(/ \\\.png\$/,<HTACCESS>)) {
|
||||
return "Dependency graph images are not accessible.\nAssuming that you have not modified the file, delete $webdotdir/.htaccess and re-run checksetup.pl to rectify.\n";
|
||||
}
|
||||
close HTACCESS;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_user_verify_class {
|
||||
# doeditparams traverses the list of params, and for each one it checks,
|
||||
# then updates. This means that if one param checker wants to look at
|
||||
# other params, it must be below that other one. So you can't have two
|
||||
# params mutually dependent on each other.
|
||||
# This means that if someone clears the LDAP config params after setting
|
||||
# the login method as LDAP, we won't notice, but all logins will fail.
|
||||
# So don't do that.
|
||||
|
||||
my $params = Bugzilla->params;
|
||||
my ($list, $entry) = @_;
|
||||
$list || return 'You need to specify at least one authentication mechanism';
|
||||
for my $class (split /,\s*/, $list) {
|
||||
my $res = check_multi($class, $entry);
|
||||
return $res if $res;
|
||||
if ($class eq 'RADIUS') {
|
||||
if (!Bugzilla->feature('auth_radius')) {
|
||||
return "RADIUS support is not available. Run checksetup.pl"
|
||||
. " for more details";
|
||||
}
|
||||
return "RADIUS servername (RADIUS_server) is missing"
|
||||
if !$params->{"RADIUS_server"};
|
||||
return "RADIUS_secret is empty" if !$params->{"RADIUS_secret"};
|
||||
}
|
||||
elsif ($class eq 'LDAP') {
|
||||
if (!Bugzilla->feature('auth_ldap')) {
|
||||
return "LDAP support is not available. Run checksetup.pl"
|
||||
. " for more details";
|
||||
}
|
||||
return "LDAP servername (LDAPserver) is missing"
|
||||
if !$params->{"LDAPserver"};
|
||||
return "LDAPBaseDN is empty" if !$params->{"LDAPBaseDN"};
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_mail_delivery_method {
|
||||
my $check = check_multi(@_);
|
||||
return $check if $check;
|
||||
my $mailer = shift;
|
||||
if ($mailer eq 'sendmail' and ON_WINDOWS) {
|
||||
# look for sendmail.exe
|
||||
return "Failed to locate " . SENDMAIL_EXE
|
||||
unless -e SENDMAIL_EXE;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_maxattachmentsize {
|
||||
my $check = check_numeric(@_);
|
||||
return $check if $check;
|
||||
my $size = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
if ($dbh->isa('Bugzilla::DB::Mysql')) {
|
||||
my (undef, $max_packet) = $dbh->selectrow_array(
|
||||
q{SHOW VARIABLES LIKE 'max\_allowed\_packet'});
|
||||
my $byte_size = $size * 1024;
|
||||
if ($max_packet < $byte_size) {
|
||||
return "You asked for a maxattachmentsize of $byte_size bytes,"
|
||||
. " but the max_allowed_packet setting in MySQL currently"
|
||||
. " only allows packets up to $max_packet bytes";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_notification {
|
||||
my $option = shift;
|
||||
my @current_version =
|
||||
(BUGZILLA_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";
|
||||
}
|
||||
if ($option ne 'disabled' && !Bugzilla->feature('updates')) {
|
||||
return "Some Perl modules are missing to get notifications about " .
|
||||
"new releases. See the output of checksetup.pl for more information";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_smtp_auth {
|
||||
my $username = shift;
|
||||
if ($username and !Bugzilla->feature('smtp_auth')) {
|
||||
return "SMTP Authentication is not available. Run checksetup.pl for"
|
||||
. " more details";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_theschwartz_available {
|
||||
my $use_queue = shift;
|
||||
if ($use_queue && !Bugzilla->feature('jobqueue')) {
|
||||
return "Using the job queue requires that you have certain Perl"
|
||||
. " modules installed. See the output of checksetup.pl"
|
||||
. " for more information";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
# OK, here are the parameter definitions themselves.
|
||||
#
|
||||
# Each definition is a hash with keys:
|
||||
#
|
||||
# name - name of the param
|
||||
# desc - description of the param (for editparams.cgi)
|
||||
# type - see below
|
||||
# choices - (optional) see below
|
||||
# default - default value for the param
|
||||
# checker - (optional) checking function for validating parameter entry
|
||||
# It is called with the value of the param as the first arg and a
|
||||
# reference to the param's hash as the second argument
|
||||
#
|
||||
# The type value can be one of the following:
|
||||
#
|
||||
# t -- A short text entry field (suitable for a single line)
|
||||
# p -- A short text entry field (as with type = 't'), but the string is
|
||||
# replaced by asterisks (appropriate for passwords)
|
||||
# l -- A long text field (suitable for many lines)
|
||||
# b -- A boolean value (either 1 or 0)
|
||||
# m -- A list of values, with many selectable (shows up as a select box)
|
||||
# To specify the list of values, make the 'choices' key be an array
|
||||
# reference of the valid choices. The 'default' key should be a string
|
||||
# with a list of selected values (as a comma-separated list), i.e.:
|
||||
# {
|
||||
# name => 'multiselect',
|
||||
# desc => 'A list of options, choose many',
|
||||
# type => 'm',
|
||||
# choices => [ 'a', 'b', 'c', 'd' ],
|
||||
# default => [ 'a', 'd' ],
|
||||
# checker => \&check_multi
|
||||
# }
|
||||
#
|
||||
# Here, 'a' and 'd' are the default options, and the user may pick any
|
||||
# combination of a, b, c, and d as valid options.
|
||||
#
|
||||
# &check_multi should always be used as the param verification function
|
||||
# for list (single and multiple) parameter types.
|
||||
#
|
||||
# o -- A list of values, orderable, and with many selectable (shows up as a
|
||||
# JavaScript-enhanced select box if JavaScript is enabled, and a text
|
||||
# entry field if not)
|
||||
# Set up in the same way as type m.
|
||||
#
|
||||
# s -- A list of values, with one selectable (shows up as a select box)
|
||||
# To specify the list of values, make the 'choices' key be an array
|
||||
# reference of the valid choices. The 'default' key should be one of
|
||||
# those values, i.e.:
|
||||
# {
|
||||
# name => 'singleselect',
|
||||
# desc => 'A list of options, choose one',
|
||||
# type => 's',
|
||||
# choices => [ 'a', 'b', 'c' ],
|
||||
# default => 'b',
|
||||
# checker => \&check_multi
|
||||
# }
|
||||
#
|
||||
# Here, 'b' is the default option, and 'a' and 'c' are other possible
|
||||
# options, but only one at a time!
|
||||
#
|
||||
# &check_multi should always be used as the param verification function
|
||||
# for list (single and multiple) parameter types.
|
||||
|
||||
sub get_param_list {
|
||||
return;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Config::Common - Parameter checking functions
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
All parameter checking functions are called with two parameters: the value to
|
||||
check, and a hash with the details of the param (type, default etc.) as defined
|
||||
in the relevant F<Bugzilla::Config::*> package.
|
||||
|
||||
=head2 Functions
|
||||
|
||||
=over
|
||||
|
||||
=item C<check_multi>
|
||||
|
||||
Checks that a multi-valued parameter (ie types C<s>, C<o> or C<m>) satisfies
|
||||
its contraints.
|
||||
|
||||
=item C<check_numeric>
|
||||
|
||||
Checks that the value is a valid number
|
||||
|
||||
=item C<check_regexp>
|
||||
|
||||
Checks that the value is a valid regexp
|
||||
|
||||
=back
|
||||
@@ -1,68 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::Core;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 100;
|
||||
|
||||
use constant get_param_list => (
|
||||
{
|
||||
name => 'urlbase',
|
||||
type => 't',
|
||||
default => '',
|
||||
checker => \&check_urlbase
|
||||
},
|
||||
|
||||
{
|
||||
name => 'ssl_redirect',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'sslbase',
|
||||
type => 't',
|
||||
default => '',
|
||||
checker => \&check_sslbase
|
||||
},
|
||||
|
||||
{
|
||||
name => 'cookiepath',
|
||||
type => 't',
|
||||
default => '/'
|
||||
},
|
||||
);
|
||||
|
||||
1;
|
||||
@@ -1,52 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::DependencyGraph;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 800;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'webdotbase',
|
||||
type => 't',
|
||||
default => 'http://www.research.att.com/~north/cgi-bin/webdot.cgi/%urlbase%',
|
||||
checker => \&check_webdotbase
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,83 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Config::General;
|
||||
use strict;
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 150;
|
||||
|
||||
use constant get_param_list => (
|
||||
{
|
||||
name => 'maintainer',
|
||||
type => 't',
|
||||
no_reset => '1',
|
||||
default => '',
|
||||
checker => \&check_email
|
||||
},
|
||||
|
||||
{
|
||||
name => 'docs_urlbase',
|
||||
type => 't',
|
||||
default => 'docs/%lang%/html/',
|
||||
checker => \&check_url
|
||||
},
|
||||
|
||||
{
|
||||
name => 'utf8',
|
||||
type => 'b',
|
||||
default => '0',
|
||||
checker => \&check_utf8
|
||||
},
|
||||
|
||||
{
|
||||
name => 'shutdownhtml',
|
||||
type => 'l',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'announcehtml',
|
||||
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
|
||||
},
|
||||
);
|
||||
|
||||
1;
|
||||
@@ -1,110 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::GroupSecurity;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
use Bugzilla::Group;
|
||||
|
||||
our $sortkey = 900;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'makeproductgroups',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'chartgroup',
|
||||
type => 's',
|
||||
choices => \&_get_all_group_names,
|
||||
default => 'editbugs',
|
||||
checker => \&check_group
|
||||
},
|
||||
|
||||
{
|
||||
name => 'insidergroup',
|
||||
type => 's',
|
||||
choices => \&_get_all_group_names,
|
||||
default => '',
|
||||
checker => \&check_group
|
||||
},
|
||||
|
||||
{
|
||||
name => 'timetrackinggroup',
|
||||
type => 's',
|
||||
choices => \&_get_all_group_names,
|
||||
default => 'editbugs',
|
||||
checker => \&check_group
|
||||
},
|
||||
|
||||
{
|
||||
name => 'querysharegroup',
|
||||
type => 's',
|
||||
choices => \&_get_all_group_names,
|
||||
default => 'editbugs',
|
||||
checker => \&check_group
|
||||
},
|
||||
|
||||
{
|
||||
name => 'debug_group',
|
||||
type => 's',
|
||||
choices => \&_get_all_group_names,
|
||||
default => 'admin',
|
||||
checker => \&check_group
|
||||
},
|
||||
|
||||
{
|
||||
name => 'usevisibilitygroups',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'strict_isolation',
|
||||
type => 'b',
|
||||
default => 0
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
sub _get_all_group_names {
|
||||
my @group_names = map {$_->name} Bugzilla::Group->get_all;
|
||||
unshift(@group_names, '');
|
||||
return \@group_names;
|
||||
}
|
||||
1;
|
||||
@@ -1,87 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::LDAP;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 1000;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'LDAPserver',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'LDAPstarttls',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'LDAPbinddn',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'LDAPBaseDN',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'LDAPuidattribute',
|
||||
type => 't',
|
||||
default => 'uid'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'LDAPmailattribute',
|
||||
type => 't',
|
||||
default => 'mail'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'LDAPfilter',
|
||||
type => 't',
|
||||
default => '',
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,108 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::MTA;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
# Return::Value 1.666002 pollutes the error log with warnings about this
|
||||
# deprecated module. We have to set NO_CLUCK = 1 before loading Email::Send
|
||||
# to disable these warnings.
|
||||
BEGIN {
|
||||
$Return::Value::NO_CLUCK = 1;
|
||||
}
|
||||
use Email::Send;
|
||||
|
||||
our $sortkey = 1200;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'mail_delivery_method',
|
||||
type => 's',
|
||||
# Bugzilla is not ready yet to send mails to newsgroups, and 'IO'
|
||||
# is of no use for now as we already have our own 'Test' mode.
|
||||
choices => [grep {$_ ne 'NNTP' && $_ ne 'IO'} Email::Send->new()->all_mailers(), 'None'],
|
||||
default => 'Sendmail',
|
||||
checker => \&check_mail_delivery_method
|
||||
},
|
||||
|
||||
{
|
||||
name => 'mailfrom',
|
||||
type => 't',
|
||||
default => 'bugzilla-daemon'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'use_mailer_queue',
|
||||
type => 'b',
|
||||
default => 0,
|
||||
checker => \&check_theschwartz_available,
|
||||
},
|
||||
|
||||
{
|
||||
name => 'smtpserver',
|
||||
type => 't',
|
||||
default => 'localhost'
|
||||
},
|
||||
{
|
||||
name => 'smtp_username',
|
||||
type => 't',
|
||||
default => '',
|
||||
checker => \&check_smtp_auth
|
||||
},
|
||||
{
|
||||
name => 'smtp_password',
|
||||
type => 'p',
|
||||
default => ''
|
||||
},
|
||||
{
|
||||
name => 'smtp_debug',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
{
|
||||
name => 'whinedays',
|
||||
type => 't',
|
||||
default => 7,
|
||||
checker => \&check_numeric
|
||||
},
|
||||
{
|
||||
name => 'globalwatchers',
|
||||
type => 't',
|
||||
default => '',
|
||||
}, );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,75 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::PatchViewer;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 1300;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'cvsroot',
|
||||
type => 't',
|
||||
default => '',
|
||||
},
|
||||
|
||||
{
|
||||
name => 'cvsroot_get',
|
||||
type => 't',
|
||||
default => '',
|
||||
},
|
||||
|
||||
{
|
||||
name => 'bonsai_url',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'lxr_url',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'lxr_root',
|
||||
type => 't',
|
||||
default => '',
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,80 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::Query;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 1400;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'quip_list_entry_control',
|
||||
type => 's',
|
||||
choices => ['open', 'moderated', 'closed'],
|
||||
default => 'open',
|
||||
checker => \&check_multi
|
||||
},
|
||||
|
||||
{
|
||||
name => 'mostfreqthreshold',
|
||||
type => 't',
|
||||
default => '2',
|
||||
checker => \&check_numeric
|
||||
},
|
||||
|
||||
{
|
||||
name => 'mybugstemplate',
|
||||
type => 't',
|
||||
default => 'buglist.cgi?resolution=---&emailassigned_to1=1&emailreporter1=1&emailtype1=exact&email1=%userid%'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'defaultquery',
|
||||
type => 't',
|
||||
default => 'resolution=---&emailassigned_to1=1&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailqa_contact2=1&emaillongdesc3=1&order=Importance&long_desc_type=substring'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'specific_search_allow_empty_words',
|
||||
type => 'b',
|
||||
default => 1
|
||||
}
|
||||
|
||||
);
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,60 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Marc Schumann.
|
||||
# Portions created by Marc Schumann are Copyright (c) 2007 Marc Schumann.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::RADIUS;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 1100;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'RADIUS_server',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'RADIUS_secret',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'RADIUS_NAS_IP',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'RADIUS_email_suffix',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
);
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,73 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::ShadowDB;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 1500;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'shadowdbhost',
|
||||
type => 't',
|
||||
default => '',
|
||||
},
|
||||
|
||||
{
|
||||
name => 'shadowdbport',
|
||||
type => 't',
|
||||
default => '3306',
|
||||
checker => \&check_numeric,
|
||||
},
|
||||
|
||||
{
|
||||
name => 'shadowdbsock',
|
||||
type => 't',
|
||||
default => '',
|
||||
},
|
||||
|
||||
# This entry must be _after_ the shadowdb{host,port,sock} settings so that
|
||||
# they can be used in the validation here
|
||||
{
|
||||
name => 'shadowdb',
|
||||
type => 't',
|
||||
default => '',
|
||||
checker => \&check_shadowdb
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,70 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joseph Heenan <joseph@heenan.me.uk>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
#
|
||||
|
||||
package Bugzilla::Config::UserMatch;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
our $sortkey = 1600;
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
{
|
||||
name => 'usemenuforusers',
|
||||
type => 'b',
|
||||
default => '0'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'ajax_user_autocompletion',
|
||||
type => 'b',
|
||||
default => '1',
|
||||
},
|
||||
|
||||
{
|
||||
name => 'maxusermatches',
|
||||
type => 't',
|
||||
default => '1000',
|
||||
checker => \&check_numeric
|
||||
},
|
||||
|
||||
{
|
||||
name => 'confirmuniqueusermatch',
|
||||
type => 'b',
|
||||
default => 1,
|
||||
} );
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,624 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jake <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Shane H. W. Travis <travis@sedsystems.ca>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
# Marc Schumann <wurblzap@gmail.com>
|
||||
|
||||
package Bugzilla::Constants;
|
||||
use strict;
|
||||
use base qw(Exporter);
|
||||
|
||||
# For bz_locations
|
||||
use File::Basename;
|
||||
|
||||
@Bugzilla::Constants::EXPORT = qw(
|
||||
BUGZILLA_VERSION
|
||||
|
||||
bz_locations
|
||||
|
||||
IS_NULL
|
||||
NOT_NULL
|
||||
|
||||
CONTROLMAPNA
|
||||
CONTROLMAPSHOWN
|
||||
CONTROLMAPDEFAULT
|
||||
CONTROLMAPMANDATORY
|
||||
|
||||
AUTH_OK
|
||||
AUTH_NODATA
|
||||
AUTH_ERROR
|
||||
AUTH_LOGINFAILED
|
||||
AUTH_DISABLED
|
||||
AUTH_NO_SUCH_USER
|
||||
AUTH_LOCKOUT
|
||||
|
||||
USER_PASSWORD_MIN_LENGTH
|
||||
|
||||
LOGIN_OPTIONAL
|
||||
LOGIN_NORMAL
|
||||
LOGIN_REQUIRED
|
||||
|
||||
LOGOUT_ALL
|
||||
LOGOUT_CURRENT
|
||||
LOGOUT_KEEP_CURRENT
|
||||
|
||||
GRANT_DIRECT
|
||||
GRANT_REGEXP
|
||||
|
||||
GROUP_MEMBERSHIP
|
||||
GROUP_BLESS
|
||||
GROUP_VISIBLE
|
||||
|
||||
MAILTO_USER
|
||||
MAILTO_GROUP
|
||||
|
||||
DEFAULT_COLUMN_LIST
|
||||
DEFAULT_QUERY_NAME
|
||||
DEFAULT_MILESTONE
|
||||
|
||||
QUERY_LIST
|
||||
LIST_OF_BUGS
|
||||
|
||||
SAVE_NUM_SEARCHES
|
||||
|
||||
COMMENT_COLS
|
||||
MAX_COMMENT_LENGTH
|
||||
|
||||
CMT_NORMAL
|
||||
CMT_DUPE_OF
|
||||
CMT_HAS_DUPE
|
||||
CMT_ATTACHMENT_CREATED
|
||||
CMT_ATTACHMENT_UPDATED
|
||||
|
||||
THROW_ERROR
|
||||
|
||||
RELATIONSHIPS
|
||||
REL_ASSIGNEE REL_QA REL_REPORTER REL_CC REL_GLOBAL_WATCHER
|
||||
REL_ANY
|
||||
|
||||
POS_EVENTS
|
||||
EVT_OTHER EVT_ADDED_REMOVED EVT_COMMENT EVT_ATTACHMENT EVT_ATTACHMENT_DATA
|
||||
EVT_PROJ_MANAGEMENT EVT_OPENED_CLOSED EVT_KEYWORD EVT_CC EVT_DEPEND_BLOCK
|
||||
EVT_BUG_CREATED
|
||||
|
||||
NEG_EVENTS
|
||||
EVT_UNCONFIRMED EVT_CHANGED_BY_ME
|
||||
|
||||
GLOBAL_EVENTS
|
||||
EVT_FLAG_REQUESTED EVT_REQUESTED_FLAG
|
||||
|
||||
ADMIN_GROUP_NAME
|
||||
PER_PRODUCT_PRIVILEGES
|
||||
|
||||
SENDMAIL_EXE
|
||||
SENDMAIL_PATH
|
||||
|
||||
FIELD_TYPE_UNKNOWN
|
||||
FIELD_TYPE_FREETEXT
|
||||
FIELD_TYPE_SINGLE_SELECT
|
||||
FIELD_TYPE_MULTI_SELECT
|
||||
FIELD_TYPE_TEXTAREA
|
||||
FIELD_TYPE_DATETIME
|
||||
FIELD_TYPE_BUG_ID
|
||||
FIELD_TYPE_BUG_URLS
|
||||
FIELD_TYPE_KEYWORDS
|
||||
|
||||
EMPTY_DATETIME_REGEX
|
||||
|
||||
ABNORMAL_SELECTS
|
||||
|
||||
TIMETRACKING_FIELDS
|
||||
|
||||
USAGE_MODE_BROWSER
|
||||
USAGE_MODE_CMDLINE
|
||||
USAGE_MODE_XMLRPC
|
||||
USAGE_MODE_EMAIL
|
||||
USAGE_MODE_JSON
|
||||
USAGE_MODE_TEST
|
||||
|
||||
ERROR_MODE_WEBPAGE
|
||||
ERROR_MODE_DIE
|
||||
ERROR_MODE_DIE_SOAP_FAULT
|
||||
ERROR_MODE_JSON_RPC
|
||||
ERROR_MODE_TEST
|
||||
|
||||
COLOR_ERROR
|
||||
|
||||
INSTALLATION_MODE_INTERACTIVE
|
||||
INSTALLATION_MODE_NON_INTERACTIVE
|
||||
|
||||
DB_MODULE
|
||||
ROOT_USER
|
||||
ON_WINDOWS
|
||||
ON_ACTIVESTATE
|
||||
|
||||
MAX_TOKEN_AGE
|
||||
MAX_LOGINCOOKIE_AGE
|
||||
MAX_SUDO_TOKEN_AGE
|
||||
MAX_LOGIN_ATTEMPTS
|
||||
LOGIN_LOCKOUT_INTERVAL
|
||||
MAX_STS_AGE
|
||||
|
||||
SAFE_PROTOCOLS
|
||||
LEGAL_CONTENT_TYPES
|
||||
|
||||
MIN_SMALLINT
|
||||
MAX_SMALLINT
|
||||
MAX_INT_32
|
||||
|
||||
MAX_LEN_QUERY_NAME
|
||||
MAX_CLASSIFICATION_SIZE
|
||||
MAX_PRODUCT_SIZE
|
||||
MAX_MILESTONE_SIZE
|
||||
MAX_COMPONENT_SIZE
|
||||
MAX_FIELD_VALUE_SIZE
|
||||
MAX_FREETEXT_LENGTH
|
||||
MAX_BUG_URL_LENGTH
|
||||
MAX_POSSIBLE_DUPLICATES
|
||||
|
||||
PASSWORD_DIGEST_ALGORITHM
|
||||
PASSWORD_SALT_LENGTH
|
||||
|
||||
CGI_URI_LIMIT
|
||||
|
||||
PRIVILEGES_REQUIRED_NONE
|
||||
PRIVILEGES_REQUIRED_REPORTER
|
||||
PRIVILEGES_REQUIRED_ASSIGNEE
|
||||
PRIVILEGES_REQUIRED_EMPOWERED
|
||||
);
|
||||
|
||||
@Bugzilla::Constants::EXPORT_OK = qw(contenttypes);
|
||||
|
||||
# CONSTANTS
|
||||
#
|
||||
# Bugzilla version
|
||||
use constant BUGZILLA_VERSION => "4.0.18";
|
||||
|
||||
# These are unique values that are unlikely to match a string or a number,
|
||||
# to be used in criteria for match() functions and other things. They start
|
||||
# and end with spaces because most Bugzilla stuff has trim() called on it,
|
||||
# so this is unlikely to match anything we get out of the DB.
|
||||
#
|
||||
# We can't use a reference, because Template Toolkit doesn't work with
|
||||
# them properly (constants.IS_NULL => {} just returns an empty string instead
|
||||
# of the reference).
|
||||
use constant IS_NULL => ' __IS_NULL__ ';
|
||||
use constant NOT_NULL => ' __NOT_NULL__ ';
|
||||
|
||||
#
|
||||
# ControlMap constants for group_control_map.
|
||||
# membercontol:othercontrol => meaning
|
||||
# Na:Na => Bugs in this product may not be restricted to this
|
||||
# group.
|
||||
# Shown:Na => Members of the group may restrict bugs
|
||||
# in this product to this group.
|
||||
# Shown:Shown => Members of the group may restrict bugs
|
||||
# in this product to this group.
|
||||
# Anyone who can enter bugs in this product may initially
|
||||
# restrict bugs in this product to this group.
|
||||
# Shown:Mandatory => Members of the group may restrict bugs
|
||||
# in this product to this group.
|
||||
# Non-members who can enter bug in this product
|
||||
# will be forced to restrict it.
|
||||
# Default:Na => Members of the group may restrict bugs in this
|
||||
# product to this group and do so by default.
|
||||
# Default:Default => Members of the group may restrict bugs in this
|
||||
# product to this group and do so by default and
|
||||
# nonmembers have this option on entry.
|
||||
# Default:Mandatory => Members of the group may restrict bugs in this
|
||||
# product to this group and do so by default.
|
||||
# Non-members who can enter bug in this product
|
||||
# will be forced to restrict it.
|
||||
# Mandatory:Mandatory => Bug will be forced into this group regardless.
|
||||
# All other combinations are illegal.
|
||||
|
||||
use constant CONTROLMAPNA => 0;
|
||||
use constant CONTROLMAPSHOWN => 1;
|
||||
use constant CONTROLMAPDEFAULT => 2;
|
||||
use constant CONTROLMAPMANDATORY => 3;
|
||||
|
||||
# See Bugzilla::Auth for docs on AUTH_*, LOGIN_* and LOGOUT_*
|
||||
|
||||
use constant AUTH_OK => 0;
|
||||
use constant AUTH_NODATA => 1;
|
||||
use constant AUTH_ERROR => 2;
|
||||
use constant AUTH_LOGINFAILED => 3;
|
||||
use constant AUTH_DISABLED => 4;
|
||||
use constant AUTH_NO_SUCH_USER => 5;
|
||||
use constant AUTH_LOCKOUT => 6;
|
||||
|
||||
# The minimum length a password must have.
|
||||
use constant USER_PASSWORD_MIN_LENGTH => 6;
|
||||
|
||||
use constant LOGIN_OPTIONAL => 0;
|
||||
use constant LOGIN_NORMAL => 1;
|
||||
use constant LOGIN_REQUIRED => 2;
|
||||
|
||||
use constant LOGOUT_ALL => 0;
|
||||
use constant LOGOUT_CURRENT => 1;
|
||||
use constant LOGOUT_KEEP_CURRENT => 2;
|
||||
|
||||
use constant GRANT_DIRECT => 0;
|
||||
use constant GRANT_REGEXP => 2;
|
||||
|
||||
use constant GROUP_MEMBERSHIP => 0;
|
||||
use constant GROUP_BLESS => 1;
|
||||
use constant GROUP_VISIBLE => 2;
|
||||
|
||||
use constant MAILTO_USER => 0;
|
||||
use constant MAILTO_GROUP => 1;
|
||||
|
||||
# The default list of columns for buglist.cgi
|
||||
use constant DEFAULT_COLUMN_LIST => (
|
||||
"bug_severity", "priority", "op_sys","assigned_to",
|
||||
"bug_status", "resolution", "short_desc"
|
||||
);
|
||||
|
||||
# Used by query.cgi and buglist.cgi as the named-query name
|
||||
# for the default settings.
|
||||
use constant DEFAULT_QUERY_NAME => '(Default query)';
|
||||
|
||||
# The default "defaultmilestone" created for products.
|
||||
use constant DEFAULT_MILESTONE => '---';
|
||||
|
||||
# The possible types for saved searches.
|
||||
use constant QUERY_LIST => 0;
|
||||
use constant LIST_OF_BUGS => 1;
|
||||
|
||||
# How many of the user's most recent searches to save.
|
||||
use constant SAVE_NUM_SEARCHES => 10;
|
||||
|
||||
# The column length for displayed (and wrapped) bug comments.
|
||||
use constant COMMENT_COLS => 80;
|
||||
# Used in _check_comment(). Gives the max length allowed for a comment.
|
||||
use constant MAX_COMMENT_LENGTH => 65535;
|
||||
|
||||
# The type of bug comments.
|
||||
use constant CMT_NORMAL => 0;
|
||||
use constant CMT_DUPE_OF => 1;
|
||||
use constant CMT_HAS_DUPE => 2;
|
||||
# Type 3 was CMT_POPULAR_VOTES, which moved to the Voting extension.
|
||||
# Type 4 was CMT_MOVED_TO, which moved to the OldBugMove extension.
|
||||
use constant CMT_ATTACHMENT_CREATED => 5;
|
||||
use constant CMT_ATTACHMENT_UPDATED => 6;
|
||||
|
||||
# Determine whether a validation routine should return 0 or throw
|
||||
# an error when the validation fails.
|
||||
use constant THROW_ERROR => 1;
|
||||
|
||||
use constant REL_ASSIGNEE => 0;
|
||||
use constant REL_QA => 1;
|
||||
use constant REL_REPORTER => 2;
|
||||
use constant REL_CC => 3;
|
||||
# REL 4 was REL_VOTER, before it was moved ino an extension.
|
||||
use constant REL_GLOBAL_WATCHER => 5;
|
||||
|
||||
# We need these strings for the X-Bugzilla-Reasons header
|
||||
# Note: this hash uses "," rather than "=>" to avoid auto-quoting of the LHS.
|
||||
# This should be accessed through Bugzilla::BugMail::relationships() instead
|
||||
# of being accessed directly.
|
||||
use constant RELATIONSHIPS => {
|
||||
REL_ASSIGNEE , "AssignedTo",
|
||||
REL_REPORTER , "Reporter",
|
||||
REL_QA , "QAcontact",
|
||||
REL_CC , "CC",
|
||||
REL_GLOBAL_WATCHER, "GlobalWatcher"
|
||||
};
|
||||
|
||||
# Used for global events like EVT_FLAG_REQUESTED
|
||||
use constant REL_ANY => 100;
|
||||
|
||||
# There are two sorts of event - positive and negative. Positive events are
|
||||
# those for which the user says "I want mail if this happens." Negative events
|
||||
# are those for which the user says "I don't want mail if this happens."
|
||||
#
|
||||
# Exactly when each event fires is defined in wants_bug_mail() in User.pm; I'm
|
||||
# not commenting them here in case the comments and the code get out of sync.
|
||||
use constant EVT_OTHER => 0;
|
||||
use constant EVT_ADDED_REMOVED => 1;
|
||||
use constant EVT_COMMENT => 2;
|
||||
use constant EVT_ATTACHMENT => 3;
|
||||
use constant EVT_ATTACHMENT_DATA => 4;
|
||||
use constant EVT_PROJ_MANAGEMENT => 5;
|
||||
use constant EVT_OPENED_CLOSED => 6;
|
||||
use constant EVT_KEYWORD => 7;
|
||||
use constant EVT_CC => 8;
|
||||
use constant EVT_DEPEND_BLOCK => 9;
|
||||
use constant EVT_BUG_CREATED => 10;
|
||||
|
||||
use constant POS_EVENTS => EVT_OTHER, EVT_ADDED_REMOVED, EVT_COMMENT,
|
||||
EVT_ATTACHMENT, EVT_ATTACHMENT_DATA,
|
||||
EVT_PROJ_MANAGEMENT, EVT_OPENED_CLOSED, EVT_KEYWORD,
|
||||
EVT_CC, EVT_DEPEND_BLOCK, EVT_BUG_CREATED;
|
||||
|
||||
use constant EVT_UNCONFIRMED => 50;
|
||||
use constant EVT_CHANGED_BY_ME => 51;
|
||||
|
||||
use constant NEG_EVENTS => EVT_UNCONFIRMED, EVT_CHANGED_BY_ME;
|
||||
|
||||
# These are the "global" flags, which aren't tied to a particular relationship.
|
||||
# and so use REL_ANY.
|
||||
use constant EVT_FLAG_REQUESTED => 100; # Flag has been requested of me
|
||||
use constant EVT_REQUESTED_FLAG => 101; # I have requested a flag
|
||||
|
||||
use constant GLOBAL_EVENTS => EVT_FLAG_REQUESTED, EVT_REQUESTED_FLAG;
|
||||
|
||||
# Default administration group name.
|
||||
use constant ADMIN_GROUP_NAME => 'admin';
|
||||
|
||||
# Privileges which can be per-product.
|
||||
use constant PER_PRODUCT_PRIVILEGES => ('editcomponents', 'editbugs', 'canconfirm');
|
||||
|
||||
# Path to sendmail.exe (Windows only)
|
||||
use constant SENDMAIL_EXE => '/usr/lib/sendmail.exe';
|
||||
# Paths to search for the sendmail binary (non-Windows)
|
||||
use constant SENDMAIL_PATH => '/usr/lib:/usr/sbin:/usr/ucblib';
|
||||
|
||||
# Field types. Match values in fielddefs.type column. These are purposely
|
||||
# not named after database column types, since Bugzilla fields comprise not
|
||||
# only storage but also logic. For example, we might add a "user" field type
|
||||
# whose values are stored in an integer column in the database but for which
|
||||
# we do more than we would do for a standard integer type (f.e. we might
|
||||
# display a user picker).
|
||||
|
||||
use constant FIELD_TYPE_UNKNOWN => 0;
|
||||
use constant FIELD_TYPE_FREETEXT => 1;
|
||||
use constant FIELD_TYPE_SINGLE_SELECT => 2;
|
||||
use constant FIELD_TYPE_MULTI_SELECT => 3;
|
||||
use constant FIELD_TYPE_TEXTAREA => 4;
|
||||
use constant FIELD_TYPE_DATETIME => 5;
|
||||
use constant FIELD_TYPE_BUG_ID => 6;
|
||||
use constant FIELD_TYPE_BUG_URLS => 7;
|
||||
use constant FIELD_TYPE_KEYWORDS => 8;
|
||||
|
||||
use constant EMPTY_DATETIME_REGEX => qr/^[0\-:\sA-Za-z]+$/;
|
||||
|
||||
# See the POD for Bugzilla::Field/is_abnormal to see why these are listed
|
||||
# here.
|
||||
use constant ABNORMAL_SELECTS => qw(
|
||||
classification
|
||||
product
|
||||
component
|
||||
);
|
||||
|
||||
# The fields from fielddefs that are blocked from non-timetracking users.
|
||||
# work_time is sometimes called actual_time.
|
||||
use constant TIMETRACKING_FIELDS =>
|
||||
qw(estimated_time remaining_time work_time actual_time
|
||||
percentage_complete deadline);
|
||||
|
||||
# The maximum number of days a token will remain valid.
|
||||
use constant MAX_TOKEN_AGE => 3;
|
||||
# How many days a logincookie will remain valid if not used.
|
||||
use constant MAX_LOGINCOOKIE_AGE => 30;
|
||||
# How many seconds (default is 6 hours) a sudo cookie remains valid.
|
||||
use constant MAX_SUDO_TOKEN_AGE => 21600;
|
||||
|
||||
# Maximum failed logins to lock account for this IP
|
||||
use constant MAX_LOGIN_ATTEMPTS => 5;
|
||||
# If the maximum login attempts occur during this many minutes, the
|
||||
# account is locked.
|
||||
use constant LOGIN_LOCKOUT_INTERVAL => 30;
|
||||
|
||||
# The maximum number of seconds the Strict-Transport-Security header
|
||||
# will remain valid. Default is one week.
|
||||
use constant MAX_STS_AGE => 604800;
|
||||
|
||||
# Protocols which are considered as safe.
|
||||
use constant SAFE_PROTOCOLS => ('afs', 'cid', 'ftp', 'gopher', 'http', 'https',
|
||||
'irc', 'ircs', 'mid', 'news', 'nntp', 'prospero',
|
||||
'telnet', 'view-source', 'wais');
|
||||
|
||||
# Valid MIME types for attachments.
|
||||
use constant LEGAL_CONTENT_TYPES => ('application', 'audio', 'image', 'message',
|
||||
'model', 'multipart', 'text', 'video');
|
||||
|
||||
use constant contenttypes =>
|
||||
{
|
||||
"html"=> "text/html" ,
|
||||
"rdf" => "application/rdf+xml" ,
|
||||
"atom"=> "application/atom+xml" ,
|
||||
"xml" => "application/xml" ,
|
||||
"js" => "application/x-javascript" ,
|
||||
"json"=> "application/json" ,
|
||||
"csv" => "text/csv" ,
|
||||
"png" => "image/png" ,
|
||||
"ics" => "text/calendar" ,
|
||||
};
|
||||
|
||||
# Usage modes. Default USAGE_MODE_BROWSER. Use with Bugzilla->usage_mode.
|
||||
use constant USAGE_MODE_BROWSER => 0;
|
||||
use constant USAGE_MODE_CMDLINE => 1;
|
||||
use constant USAGE_MODE_XMLRPC => 2;
|
||||
use constant USAGE_MODE_EMAIL => 3;
|
||||
use constant USAGE_MODE_JSON => 4;
|
||||
use constant USAGE_MODE_TEST => 5;
|
||||
|
||||
# Error modes. Default set by Bugzilla->usage_mode (so ERROR_MODE_WEBPAGE
|
||||
# usually). Use with Bugzilla->error_mode.
|
||||
use constant ERROR_MODE_WEBPAGE => 0;
|
||||
use constant ERROR_MODE_DIE => 1;
|
||||
use constant ERROR_MODE_DIE_SOAP_FAULT => 2;
|
||||
use constant ERROR_MODE_JSON_RPC => 3;
|
||||
use constant ERROR_MODE_TEST => 4;
|
||||
|
||||
# The ANSI colors of messages that command-line scripts use
|
||||
use constant COLOR_ERROR => 'red';
|
||||
|
||||
# The various modes that checksetup.pl can run in.
|
||||
use constant INSTALLATION_MODE_INTERACTIVE => 0;
|
||||
use constant INSTALLATION_MODE_NON_INTERACTIVE => 1;
|
||||
|
||||
# Data about what we require for different databases.
|
||||
use constant DB_MODULE => {
|
||||
'mysql' => {db => 'Bugzilla::DB::Mysql', db_version => '4.1.2',
|
||||
dbd => {
|
||||
package => 'DBD-mysql',
|
||||
module => 'DBD::mysql',
|
||||
# Disallow development versions
|
||||
blacklist => ['_'],
|
||||
# For UTF-8 support
|
||||
version => '4.00',
|
||||
},
|
||||
name => 'MySQL'},
|
||||
# Also see Bugzilla::DB::Pg::bz_check_server_version, which has special
|
||||
# code to require DBD::Pg 2.17.2 for PostgreSQL 9 and above.
|
||||
'pg' => {db => 'Bugzilla::DB::Pg', db_version => '8.00.0000',
|
||||
dbd => {
|
||||
package => 'DBD-Pg',
|
||||
module => 'DBD::Pg',
|
||||
version => '1.45',
|
||||
},
|
||||
name => 'PostgreSQL'},
|
||||
'oracle'=> {db => 'Bugzilla::DB::Oracle', db_version => '10.02.0',
|
||||
dbd => {
|
||||
package => 'DBD-Oracle',
|
||||
module => 'DBD::Oracle',
|
||||
version => '1.19',
|
||||
},
|
||||
name => 'Oracle'},
|
||||
};
|
||||
|
||||
# True if we're on Win32.
|
||||
use constant ON_WINDOWS => ($^O =~ /MSWin32/i);
|
||||
# True if we're using ActiveState Perl (as opposed to Strawberry) on Windows.
|
||||
use constant ON_ACTIVESTATE => eval { &Win32::BuildNumber };
|
||||
|
||||
# The user who should be considered "root" when we're giving
|
||||
# instructions to Bugzilla administrators.
|
||||
use constant ROOT_USER => ON_WINDOWS ? 'Administrator' : 'root';
|
||||
|
||||
use constant MIN_SMALLINT => -32768;
|
||||
use constant MAX_SMALLINT => 32767;
|
||||
use constant MAX_INT_32 => 2147483647;
|
||||
|
||||
# The longest that a saved search name can be.
|
||||
use constant MAX_LEN_QUERY_NAME => 64;
|
||||
|
||||
# The longest classification name allowed.
|
||||
use constant MAX_CLASSIFICATION_SIZE => 64;
|
||||
|
||||
# The longest product name allowed.
|
||||
use constant MAX_PRODUCT_SIZE => 64;
|
||||
|
||||
# The longest milestone name allowed.
|
||||
use constant MAX_MILESTONE_SIZE => 20;
|
||||
|
||||
# The longest component name allowed.
|
||||
use constant MAX_COMPONENT_SIZE => 64;
|
||||
|
||||
# The maximum length for values of <select> fields.
|
||||
use constant MAX_FIELD_VALUE_SIZE => 64;
|
||||
|
||||
# Maximum length allowed for free text fields.
|
||||
use constant MAX_FREETEXT_LENGTH => 255;
|
||||
|
||||
# The longest a bug URL in a BUG_URLS field can be.
|
||||
use constant MAX_BUG_URL_LENGTH => 255;
|
||||
|
||||
# The largest number of possible duplicates that Bug::possible_duplicates
|
||||
# will return.
|
||||
use constant MAX_POSSIBLE_DUPLICATES => 25;
|
||||
|
||||
# This is the name of the algorithm used to hash passwords before storing
|
||||
# them in the database. This can be any string that is valid to pass to
|
||||
# Perl's "Digest" module. Note that if you change this, it won't take
|
||||
# effect until a user changes his password.
|
||||
use constant PASSWORD_DIGEST_ALGORITHM => 'SHA-256';
|
||||
# How long of a salt should we use? Note that if you change this, none
|
||||
# of your users will be able to log in until they reset their passwords.
|
||||
use constant PASSWORD_SALT_LENGTH => 8;
|
||||
|
||||
# Certain scripts redirect to GET even if the form was submitted originally
|
||||
# via POST such as buglist.cgi. This value determines whether the redirect
|
||||
# can be safely done or not based on the web server's URI length setting.
|
||||
use constant CGI_URI_LIMIT => 8000;
|
||||
|
||||
# If the user isn't allowed to change a field, we must tell him who can.
|
||||
# We store the required permission set into the $PrivilegesRequired
|
||||
# variable which gets passed to the error template.
|
||||
|
||||
use constant PRIVILEGES_REQUIRED_NONE => 0;
|
||||
use constant PRIVILEGES_REQUIRED_REPORTER => 1;
|
||||
use constant PRIVILEGES_REQUIRED_ASSIGNEE => 2;
|
||||
use constant PRIVILEGES_REQUIRED_EMPOWERED => 3;
|
||||
|
||||
sub bz_locations {
|
||||
# We know that Bugzilla/Constants.pm must be in %INC at this point.
|
||||
# So the only question is, what's the name of the directory
|
||||
# above it? This is the most reliable way to get our current working
|
||||
# directory under both mod_cgi and mod_perl. We call dirname twice
|
||||
# to get the name of the directory above the "Bugzilla/" directory.
|
||||
#
|
||||
# Calling dirname twice like that won't work on VMS or AmigaOS
|
||||
# but I doubt anybody runs Bugzilla on those.
|
||||
#
|
||||
# On mod_cgi this will be a relative path. On mod_perl it will be an
|
||||
# absolute path.
|
||||
my $libpath = dirname(dirname($INC{'Bugzilla/Constants.pm'}));
|
||||
# We have to detaint $libpath, but we can't use Bugzilla::Util here.
|
||||
$libpath =~ /(.*)/;
|
||||
$libpath = $1;
|
||||
|
||||
my ($project, $localconfig, $datadir);
|
||||
if ($ENV{'PROJECT'} && $ENV{'PROJECT'} =~ /^(\w+)$/) {
|
||||
$project = $1;
|
||||
$localconfig = "localconfig.$project";
|
||||
$datadir = "data/$project";
|
||||
} else {
|
||||
$localconfig = "localconfig";
|
||||
$datadir = "data";
|
||||
}
|
||||
|
||||
# We have to return absolute paths for mod_perl.
|
||||
# That means that if you modify these paths, they must be absolute paths.
|
||||
return {
|
||||
'libpath' => $libpath,
|
||||
'ext_libpath' => "$libpath/lib",
|
||||
# If you put the libraries in a different location than the CGIs,
|
||||
# make sure this still points to the CGIs.
|
||||
'cgi_path' => $libpath,
|
||||
'templatedir' => "$libpath/template",
|
||||
'project' => $project,
|
||||
'localconfig' => "$libpath/$localconfig",
|
||||
'datadir' => "$libpath/$datadir",
|
||||
'attachdir' => "$libpath/$datadir/attachments",
|
||||
'skinsdir' => "$libpath/skins",
|
||||
'graphsdir' => "$libpath/graphs",
|
||||
# $webdotdir must be in the web server's tree somewhere. Even if you use a
|
||||
# local dot, we output images to there. Also, if $webdotdir is
|
||||
# not relative to the bugzilla root directory, you'll need to
|
||||
# change showdependencygraph.cgi to set image_url to the correct
|
||||
# location.
|
||||
# The script should really generate these graphs directly...
|
||||
'webdotdir' => "$libpath/$datadir/webdot",
|
||||
'extensionsdir' => "$libpath/extensions",
|
||||
};
|
||||
}
|
||||
|
||||
1;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,750 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Oracle Corporation.
|
||||
# Portions created by Oracle are Copyright (C) 2007 Oracle Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Lance Larsh <lance.larsh@oracle.com>
|
||||
# Xiaoou Wu <xiaoou.wu@oracle.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::DB::Oracle - Bugzilla database compatibility layer for Oracle
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module overrides methods of the Bugzilla::DB module with Oracle
|
||||
specific implementation. It is instantiated by the Bugzilla::DB module
|
||||
and should never be used directly.
|
||||
|
||||
For interface details see L<Bugzilla::DB> and L<DBI>.
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::DB::Oracle;
|
||||
use strict;
|
||||
use base qw(Bugzilla::DB);
|
||||
|
||||
use DBD::Oracle;
|
||||
use DBD::Oracle qw(:ora_types);
|
||||
use List::Util qw(max);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
|
||||
#####################################################################
|
||||
# Constants
|
||||
#####################################################################
|
||||
use constant EMPTY_STRING => '__BZ_EMPTY_STR__';
|
||||
use constant ISOLATION_LEVEL => 'READ COMMITTED';
|
||||
use constant BLOB_TYPE => { ora_type => ORA_BLOB };
|
||||
# The max size allowed for LOB fields, in kilobytes.
|
||||
use constant MIN_LONG_READ_LEN => 32 * 1024;
|
||||
use constant FULLTEXT_OR => ' OR ';
|
||||
|
||||
sub new {
|
||||
my ($class, $params) = @_;
|
||||
my ($user, $pass, $host, $dbname, $port) =
|
||||
@$params{qw(db_user db_pass db_host db_name db_port)};
|
||||
|
||||
# You can never connect to Oracle without a DB name,
|
||||
# and there is no default DB.
|
||||
$dbname ||= Bugzilla->localconfig->{db_name};
|
||||
|
||||
# Set the language enviroment
|
||||
$ENV{'NLS_LANG'} = '.AL32UTF8' if Bugzilla->params->{'utf8'};
|
||||
|
||||
# construct the DSN from the parameters we got
|
||||
my $dsn = "dbi:Oracle:host=$host;sid=$dbname";
|
||||
$dsn .= ";port=$port" if $port;
|
||||
my $attrs = { FetchHashKeyName => 'NAME_lc',
|
||||
LongReadLen => max(Bugzilla->params->{'maxattachmentsize'},
|
||||
MIN_LONG_READ_LEN) * 1024,
|
||||
};
|
||||
my $self = $class->db_new({ dsn => $dsn, user => $user,
|
||||
pass => $pass, attrs => $attrs });
|
||||
# Needed by TheSchwartz
|
||||
$self->{private_bz_dsn} = $dsn;
|
||||
|
||||
bless ($self, $class);
|
||||
|
||||
# Set the session's default date format to match MySQL
|
||||
$self->do("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
|
||||
$self->do("ALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS'");
|
||||
$self->do("ALTER SESSION SET NLS_LENGTH_SEMANTICS='CHAR'")
|
||||
if Bugzilla->params->{'utf8'};
|
||||
# To allow case insensitive query.
|
||||
$self->do("ALTER SESSION SET NLS_COMP='ANSI'");
|
||||
$self->do("ALTER SESSION SET NLS_SORT='BINARY_AI'");
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub bz_last_key {
|
||||
my ($self, $table, $column) = @_;
|
||||
|
||||
my $seq = $table . "_" . $column . "_SEQ";
|
||||
my ($last_insert_id) = $self->selectrow_array("SELECT $seq.CURRVAL "
|
||||
. " FROM DUAL");
|
||||
return $last_insert_id;
|
||||
}
|
||||
|
||||
sub bz_check_regexp {
|
||||
my ($self, $pattern) = @_;
|
||||
|
||||
eval { $self->do("SELECT 1 FROM DUAL WHERE "
|
||||
. $self->sql_regexp($self->quote("a"), $pattern, 1)) };
|
||||
|
||||
$@ && ThrowUserError('illegal_regexp',
|
||||
{ value => $pattern, dberror => $self->errstr });
|
||||
}
|
||||
|
||||
sub bz_explain {
|
||||
my ($self, $sql) = @_;
|
||||
my $sth = $self->prepare("EXPLAIN PLAN FOR $sql");
|
||||
$sth->execute();
|
||||
my $explain = $self->selectcol_arrayref(
|
||||
"SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY)");
|
||||
return join("\n", @$explain);
|
||||
}
|
||||
|
||||
sub sql_group_concat {
|
||||
my ($self, $text, $separator) = @_;
|
||||
$separator = $self->quote(', ') if !defined $separator;
|
||||
return "group_concat(T_CLOB_DELIM($text, $separator))";
|
||||
}
|
||||
|
||||
sub sql_regexp {
|
||||
my ($self, $expr, $pattern, $nocheck, $real_pattern) = @_;
|
||||
$real_pattern ||= $pattern;
|
||||
|
||||
$self->bz_check_regexp($real_pattern) if !$nocheck;
|
||||
|
||||
return "REGEXP_LIKE($expr, $pattern)";
|
||||
}
|
||||
|
||||
sub sql_not_regexp {
|
||||
my ($self, $expr, $pattern, $nocheck, $real_pattern) = @_;
|
||||
$real_pattern ||= $pattern;
|
||||
|
||||
$self->bz_check_regexp($real_pattern) if !$nocheck;
|
||||
|
||||
return "NOT REGEXP_LIKE($expr, $pattern)"
|
||||
}
|
||||
|
||||
sub sql_limit {
|
||||
my ($self, $limit, $offset) = @_;
|
||||
|
||||
if(defined $offset) {
|
||||
return "/* LIMIT $limit $offset */";
|
||||
}
|
||||
return "/* LIMIT $limit */";
|
||||
}
|
||||
|
||||
sub sql_string_concat {
|
||||
my ($self, @params) = @_;
|
||||
|
||||
return 'CONCAT(' . join(', ', @params) . ')';
|
||||
}
|
||||
|
||||
sub sql_to_days {
|
||||
my ($self, $date) = @_;
|
||||
|
||||
return " TO_CHAR(TO_DATE($date),'J') ";
|
||||
}
|
||||
sub sql_from_days{
|
||||
my ($self, $date) = @_;
|
||||
|
||||
return " TO_DATE($date,'J') ";
|
||||
}
|
||||
sub sql_fulltext_search {
|
||||
my ($self, $column, $text, $label) = @_;
|
||||
$text = $self->quote($text);
|
||||
trick_taint($text);
|
||||
return "CONTAINS($column,$text,$label) > 0", "SCORE($label)";
|
||||
}
|
||||
|
||||
sub sql_date_format {
|
||||
my ($self, $date, $format) = @_;
|
||||
|
||||
$format = "%Y.%m.%d %H:%i:%s" if !$format;
|
||||
|
||||
$format =~ s/\%Y/YYYY/g;
|
||||
$format =~ s/\%y/YY/g;
|
||||
$format =~ s/\%m/MM/g;
|
||||
$format =~ s/\%d/DD/g;
|
||||
$format =~ s/\%a/Dy/g;
|
||||
$format =~ s/\%H/HH24/g;
|
||||
$format =~ s/\%i/MI/g;
|
||||
$format =~ s/\%s/SS/g;
|
||||
|
||||
return "TO_CHAR($date, " . $self->quote($format) . ")";
|
||||
}
|
||||
|
||||
sub sql_interval {
|
||||
my ($self, $interval, $units) = @_;
|
||||
if ($units =~ /YEAR|MONTH/i) {
|
||||
return "NUMTOYMINTERVAL($interval,'$units')";
|
||||
} else{
|
||||
return "NUMTODSINTERVAL($interval,'$units')";
|
||||
}
|
||||
}
|
||||
|
||||
sub sql_position {
|
||||
my ($self, $fragment, $text) = @_;
|
||||
return "INSTR($text, $fragment)";
|
||||
}
|
||||
|
||||
sub sql_in {
|
||||
my ($self, $column_name, $in_list_ref) = @_;
|
||||
my @in_list = @$in_list_ref;
|
||||
return $self->SUPER::sql_in($column_name, $in_list_ref) if $#in_list < 1000;
|
||||
my @in_str;
|
||||
while (@in_list) {
|
||||
my $length = $#in_list + 1;
|
||||
my $splice = $length > 1000 ? 1000 : $length;
|
||||
my @sub_in_list = splice(@in_list, 0, $splice);
|
||||
push(@in_str,
|
||||
$self->SUPER::sql_in($column_name, \@sub_in_list));
|
||||
}
|
||||
return "( " . join(" OR ", @in_str) . " )";
|
||||
}
|
||||
|
||||
sub _bz_add_field_table {
|
||||
my ($self, $name, $schema_ref, $type) = @_;
|
||||
$self->SUPER::_bz_add_field_table($name, $schema_ref);
|
||||
if (defined($type) && $type == FIELD_TYPE_MULTI_SELECT) {
|
||||
my $uk_name = "UK_" . $self->_bz_schema->_hash_identifier($name . '_value');
|
||||
$self->do("ALTER TABLE $name ADD CONSTRAINT $uk_name UNIQUE(value)");
|
||||
}
|
||||
}
|
||||
|
||||
sub bz_drop_table {
|
||||
my ($self, $name) = @_;
|
||||
my $table_exists = $self->bz_table_info($name);
|
||||
if ($table_exists) {
|
||||
$self->_bz_drop_fks($name);
|
||||
$self->SUPER::bz_drop_table($name);
|
||||
}
|
||||
}
|
||||
|
||||
# Dropping all FKs for a specified table.
|
||||
sub _bz_drop_fks {
|
||||
my ($self, $table) = @_;
|
||||
my @columns = $self->_bz_real_schema->get_table_columns($table);
|
||||
foreach my $column (@columns) {
|
||||
$self->bz_drop_fk($table, $column);
|
||||
}
|
||||
}
|
||||
|
||||
sub _fix_empty {
|
||||
my ($string) = @_;
|
||||
$string = '' if $string eq EMPTY_STRING;
|
||||
return $string;
|
||||
}
|
||||
|
||||
sub _fix_arrayref {
|
||||
my ($row) = @_;
|
||||
return undef if !defined $row;
|
||||
foreach my $field (@$row) {
|
||||
$field = _fix_empty($field) if defined $field;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
sub _fix_hashref {
|
||||
my ($row) = @_;
|
||||
return undef if !defined $row;
|
||||
foreach my $value (values %$row) {
|
||||
$value = _fix_empty($value) if defined $value;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
sub adjust_statement {
|
||||
my ($sql) = @_;
|
||||
|
||||
if ($sql =~ /^CREATE OR REPLACE.*/i){
|
||||
return $sql;
|
||||
}
|
||||
|
||||
# We can't just assume any occurrence of "''" in $sql is an empty
|
||||
# string, since "''" can occur inside a string literal as a way of
|
||||
# escaping a single "'" in the literal. Therefore we must be trickier...
|
||||
|
||||
# split the statement into parts by single-quotes. The negative value
|
||||
# at the end to the split operator from dropping trailing empty strings
|
||||
# (e.g., when $sql ends in "''")
|
||||
my @parts = split /'/, $sql, -1;
|
||||
|
||||
if( !(@parts % 2) ) {
|
||||
# Either the string is empty or the quotes are mismatched
|
||||
# Returning input unmodified.
|
||||
return $sql;
|
||||
}
|
||||
|
||||
# We already verified that we have an odd number of parts. If we take
|
||||
# the first part off now, we know we're entering the loop with an even
|
||||
# number of parts
|
||||
my @result;
|
||||
my $part = shift @parts;
|
||||
|
||||
# Oracle requires a FROM clause in all SELECT statements, so append
|
||||
# "FROM dual" to queries without one (e.g., "SELECT NOW()")
|
||||
my $is_select = ($part =~ m/^\s*SELECT\b/io);
|
||||
my $has_from = ($part =~ m/\bFROM\b/io) if $is_select;
|
||||
|
||||
# Oracle recognizes CURRENT_DATE, but not CURRENT_DATE()
|
||||
$part =~ s/\bCURRENT_DATE\b\(\)/CURRENT_DATE/io;
|
||||
|
||||
# Oracle use SUBSTR instead of SUBSTRING
|
||||
$part =~ s/\bSUBSTRING\b/SUBSTR/io;
|
||||
|
||||
# Oracle need no 'AS'
|
||||
$part =~ s/\bAS\b//ig;
|
||||
|
||||
# Oracle doesn't have LIMIT, so if we find the LIMIT comment, wrap the
|
||||
# query with "SELECT * FROM (...) WHERE rownum < $limit"
|
||||
my ($limit,$offset) = ($part =~ m{/\* LIMIT (\d*) (\d*) \*/}o);
|
||||
|
||||
push @result, $part;
|
||||
while( @parts ) {
|
||||
my $string = shift @parts;
|
||||
my $nonstring = shift @parts;
|
||||
|
||||
# if the non-string part is zero-length and there are more parts left,
|
||||
# then this is an escaped quote inside a string literal
|
||||
while( !(length $nonstring) && @parts ) {
|
||||
# we know it's safe to remove two parts at a time, since we
|
||||
# entered the loop with an even number of parts
|
||||
$string .= "''" . shift @parts;
|
||||
$nonstring = shift @parts;
|
||||
}
|
||||
|
||||
# Look for a FROM if this is a SELECT and we haven't found one yet
|
||||
$has_from = ($nonstring =~ m/\bFROM\b/io)
|
||||
if ($is_select and !$has_from);
|
||||
|
||||
# Oracle recognizes CURRENT_DATE, but not CURRENT_DATE()
|
||||
$nonstring =~ s/\bCURRENT_DATE\b\(\)/CURRENT_DATE/io;
|
||||
|
||||
# Oracle use SUBSTR instead of SUBSTRING
|
||||
$nonstring =~ s/\bSUBSTRING\b/SUBSTR/io;
|
||||
|
||||
# Oracle need no 'AS'
|
||||
$nonstring =~ s/\bAS\b//ig;
|
||||
|
||||
# Take the first 4000 chars for comparison
|
||||
$nonstring =~ s/\(\s*(longdescs_\d+\.thetext|attachdata_\d+\.thedata)/
|
||||
\(DBMS_LOB.SUBSTR\($1, 4000, 1\)/ig;
|
||||
|
||||
# Look for a LIMIT clause
|
||||
($limit) = ($nonstring =~ m(/\* LIMIT (\d*) \*/)o);
|
||||
|
||||
if(!length($string)){
|
||||
push @result, EMPTY_STRING;
|
||||
push @result, $nonstring;
|
||||
} else {
|
||||
push @result, $string;
|
||||
push @result, $nonstring;
|
||||
}
|
||||
}
|
||||
|
||||
my $new_sql = join "'", @result;
|
||||
|
||||
# Append "FROM dual" if this is a SELECT without a FROM clause
|
||||
$new_sql .= " FROM DUAL" if ($is_select and !$has_from);
|
||||
|
||||
# Wrap the query with a "WHERE rownum <= ..." if we found LIMIT
|
||||
|
||||
if (defined($limit)) {
|
||||
if ($new_sql !~ /\bWHERE\b/) {
|
||||
$new_sql = $new_sql." WHERE 1=1";
|
||||
}
|
||||
my ($before_where, $after_where) = split(/\bWHERE\b/i, $new_sql, 2);
|
||||
if (defined($offset)) {
|
||||
my ($before_from, $after_from) = split(/\bFROM\b/i, $new_sql, 2);
|
||||
$before_where = "$before_from FROM ($before_from,"
|
||||
. " ROW_NUMBER() OVER (ORDER BY 1) R "
|
||||
. " FROM $after_from ) ";
|
||||
$after_where = " R BETWEEN $offset+1 AND $limit+$offset";
|
||||
} else {
|
||||
$after_where = " rownum <=$limit AND ".$after_where;
|
||||
}
|
||||
$new_sql = $before_where." WHERE ".$after_where;
|
||||
}
|
||||
return $new_sql;
|
||||
}
|
||||
|
||||
sub do {
|
||||
my $self = shift;
|
||||
my $sql = shift;
|
||||
$sql = adjust_statement($sql);
|
||||
unshift @_, $sql;
|
||||
return $self->SUPER::do(@_);
|
||||
}
|
||||
|
||||
sub selectrow_array {
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
unshift @_, $new_stmt;
|
||||
if ( wantarray ) {
|
||||
my @row = $self->SUPER::selectrow_array(@_);
|
||||
_fix_arrayref(\@row);
|
||||
return @row;
|
||||
} else {
|
||||
my $row = $self->SUPER::selectrow_array(@_);
|
||||
$row = _fix_empty($row) if defined $row;
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
sub selectrow_arrayref {
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
unshift @_, $new_stmt;
|
||||
my $ref = $self->SUPER::selectrow_arrayref(@_);
|
||||
return undef if !defined $ref;
|
||||
|
||||
_fix_arrayref($ref);
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub selectrow_hashref {
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
unshift @_, $new_stmt;
|
||||
my $ref = $self->SUPER::selectrow_hashref(@_);
|
||||
return undef if !defined $ref;
|
||||
|
||||
_fix_hashref($ref);
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub selectall_arrayref {
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
unshift @_, $new_stmt;
|
||||
my $ref = $self->SUPER::selectall_arrayref(@_);
|
||||
return undef if !defined $ref;
|
||||
|
||||
foreach my $row (@$ref) {
|
||||
if (ref($row) eq 'ARRAY') {
|
||||
_fix_arrayref($row);
|
||||
}
|
||||
elsif (ref($row) eq 'HASH') {
|
||||
_fix_hashref($row);
|
||||
}
|
||||
}
|
||||
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub selectall_hashref {
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
unshift @_, $new_stmt;
|
||||
my $rows = $self->SUPER::selectall_hashref(@_);
|
||||
return undef if !defined $rows;
|
||||
foreach my $row (values %$rows) {
|
||||
_fix_hashref($row);
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
sub selectcol_arrayref {
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
unshift @_, $new_stmt;
|
||||
my $ref = $self->SUPER::selectcol_arrayref(@_);
|
||||
return undef if !defined $ref;
|
||||
_fix_arrayref($ref);
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub prepare {
|
||||
my $self = shift;
|
||||
my $sql = shift;
|
||||
my $new_sql = adjust_statement($sql);
|
||||
unshift @_, $new_sql;
|
||||
return bless $self->SUPER::prepare(@_),
|
||||
'Bugzilla::DB::Oracle::st';
|
||||
}
|
||||
|
||||
sub prepare_cached {
|
||||
my $self = shift;
|
||||
my $sql = shift;
|
||||
my $new_sql = adjust_statement($sql);
|
||||
unshift @_, $new_sql;
|
||||
return bless $self->SUPER::prepare_cached(@_),
|
||||
'Bugzilla::DB::Oracle::st';
|
||||
}
|
||||
|
||||
sub quote_identifier {
|
||||
my ($self,$id) = @_;
|
||||
return $id;
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# Protected "Real Database" Schema Information Methods
|
||||
#####################################################################
|
||||
|
||||
sub bz_table_columns_real {
|
||||
my ($self, $table) = @_;
|
||||
$table = uc($table);
|
||||
my $cols = $self->selectcol_arrayref(
|
||||
"SELECT LOWER(COLUMN_NAME) FROM USER_TAB_COLUMNS WHERE
|
||||
TABLE_NAME = ? ORDER BY COLUMN_NAME", undef, $table);
|
||||
return @$cols;
|
||||
}
|
||||
|
||||
sub bz_table_list_real {
|
||||
my ($self) = @_;
|
||||
my $tables = $self->selectcol_arrayref(
|
||||
"SELECT LOWER(TABLE_NAME) FROM USER_TABLES WHERE
|
||||
TABLE_NAME NOT LIKE ? ORDER BY TABLE_NAME", undef, 'DR$%');
|
||||
return @$tables;
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# Custom Database Setup
|
||||
#####################################################################
|
||||
|
||||
sub bz_setup_database {
|
||||
my $self = shift;
|
||||
|
||||
# Create a function that returns SYSDATE to emulate MySQL's "NOW()".
|
||||
# Function NOW() is used widely in Bugzilla SQLs, but Oracle does not
|
||||
# have that function, So we have to create one ourself.
|
||||
$self->do("CREATE OR REPLACE FUNCTION NOW "
|
||||
. " RETURN DATE IS BEGIN RETURN SYSDATE; END;");
|
||||
$self->do("CREATE OR REPLACE FUNCTION CHAR_LENGTH(COLUMN_NAME VARCHAR2)"
|
||||
. " RETURN NUMBER IS BEGIN RETURN LENGTH(COLUMN_NAME); END;");
|
||||
|
||||
# Create types for group_concat
|
||||
my $t_clob_delim = $self->selectcol_arrayref("
|
||||
SELECT TYPE_NAME FROM USER_TYPES WHERE TYPE_NAME=?",
|
||||
undef, 'T_CLOB_DELIM');
|
||||
|
||||
if ( !@$t_clob_delim ) {
|
||||
$self->do("CREATE OR REPLACE TYPE T_CLOB_DELIM AS OBJECT "
|
||||
. "( p_CONTENT CLOB, p_DELIMITER VARCHAR2(256));");
|
||||
}
|
||||
|
||||
$self->do("CREATE OR REPLACE TYPE T_GROUP_CONCAT AS OBJECT
|
||||
( CLOB_CONTENT CLOB,
|
||||
DELIMITER VARCHAR2(256),
|
||||
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(
|
||||
SCTX IN OUT NOCOPY T_GROUP_CONCAT)
|
||||
RETURN NUMBER,
|
||||
MEMBER FUNCTION ODCIAGGREGATEITERATE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
VALUE IN T_CLOB_DELIM)
|
||||
RETURN NUMBER,
|
||||
MEMBER FUNCTION ODCIAGGREGATETERMINATE(
|
||||
SELF IN T_GROUP_CONCAT,
|
||||
RETURNVALUE OUT NOCOPY CLOB,
|
||||
FLAGS IN NUMBER)
|
||||
RETURN NUMBER,
|
||||
MEMBER FUNCTION ODCIAGGREGATEMERGE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
CTX2 IN T_GROUP_CONCAT)
|
||||
RETURN NUMBER);");
|
||||
|
||||
$self->do("CREATE OR REPLACE TYPE BODY T_GROUP_CONCAT IS
|
||||
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(
|
||||
SCTX IN OUT NOCOPY T_GROUP_CONCAT)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
SCTX := T_GROUP_CONCAT(EMPTY_CLOB(), NULL);
|
||||
DBMS_LOB.CREATETEMPORARY(SCTX.CLOB_CONTENT, TRUE);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
MEMBER FUNCTION ODCIAGGREGATEITERATE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
VALUE IN T_CLOB_DELIM)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
SELF.DELIMITER := VALUE.P_DELIMITER;
|
||||
DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT,
|
||||
LENGTH(SELF.DELIMITER),
|
||||
SELF.DELIMITER);
|
||||
DBMS_LOB.APPEND(SELF.CLOB_CONTENT, VALUE.P_CONTENT);
|
||||
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
MEMBER FUNCTION ODCIAGGREGATETERMINATE(
|
||||
SELF IN T_GROUP_CONCAT,
|
||||
RETURNVALUE OUT NOCOPY CLOB,
|
||||
FLAGS IN NUMBER)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
RETURNVALUE := RTRIM(LTRIM(SELF.CLOB_CONTENT,
|
||||
SELF.DELIMITER),
|
||||
SELF.DELIMITER);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
MEMBER FUNCTION ODCIAGGREGATEMERGE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
CTX2 IN T_GROUP_CONCAT)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT,
|
||||
LENGTH(SELF.DELIMITER),
|
||||
SELF.DELIMITER);
|
||||
DBMS_LOB.APPEND(SELF.CLOB_CONTENT, CTX2.CLOB_CONTENT);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
END;");
|
||||
|
||||
# Create user-defined aggregate function group_concat
|
||||
$self->do("CREATE OR REPLACE FUNCTION GROUP_CONCAT(P_INPUT T_CLOB_DELIM)
|
||||
RETURN CLOB
|
||||
DETERMINISTIC PARALLEL_ENABLE AGGREGATE USING T_GROUP_CONCAT;");
|
||||
|
||||
# Create a WORLD_LEXER named BZ_LEX for multilingual fulltext search
|
||||
my $lexer = $self->selectcol_arrayref(
|
||||
"SELECT pre_name FROM CTXSYS.CTX_PREFERENCES WHERE pre_name = ? AND
|
||||
pre_owner = ?",
|
||||
undef,'BZ_LEX',uc(Bugzilla->localconfig->{db_user}));
|
||||
if(!@$lexer) {
|
||||
$self->do("BEGIN CTX_DDL.CREATE_PREFERENCE
|
||||
('BZ_LEX', 'WORLD_LEXER'); END;");
|
||||
}
|
||||
|
||||
$self->SUPER::bz_setup_database(@_);
|
||||
|
||||
my @tables = $self->bz_table_list_real();
|
||||
foreach my $table (@tables) {
|
||||
my @columns = $self->bz_table_columns_real($table);
|
||||
foreach my $column (@columns) {
|
||||
my $def = $self->bz_column_info($table, $column);
|
||||
if ($def->{REFERENCES}) {
|
||||
my $references = $def->{REFERENCES};
|
||||
my $update = $references->{UPDATE} || 'CASCADE';
|
||||
my $to_table = $references->{TABLE};
|
||||
my $to_column = $references->{COLUMN};
|
||||
my $fk_name = $self->_bz_schema->_get_fk_name($table,
|
||||
$column,
|
||||
$references);
|
||||
if ( $update =~ /CASCADE/i ){
|
||||
my $trigger_name = uc($fk_name . "_UC");
|
||||
my $exist_trigger = $self->selectcol_arrayref(
|
||||
"SELECT OBJECT_NAME FROM USER_OBJECTS
|
||||
WHERE OBJECT_NAME = ?", undef, $trigger_name);
|
||||
if(@$exist_trigger) {
|
||||
$self->do("DROP TRIGGER $trigger_name");
|
||||
}
|
||||
|
||||
my $tr_str = "CREATE OR REPLACE TRIGGER $trigger_name"
|
||||
. " AFTER UPDATE OF $to_column ON $to_table "
|
||||
. " REFERENCING "
|
||||
. " NEW AS NEW "
|
||||
. " OLD AS OLD "
|
||||
. " FOR EACH ROW "
|
||||
. " BEGIN "
|
||||
. " UPDATE $table"
|
||||
. " SET $column = :NEW.$to_column"
|
||||
. " WHERE $column = :OLD.$to_column;"
|
||||
. " END $trigger_name;";
|
||||
$self->do($tr_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Drop the trigger which causes bug 541553
|
||||
my $trigger_name = "PRODUCTS_MILESTONEURL";
|
||||
my $exist_trigger = $self->selectcol_arrayref(
|
||||
"SELECT OBJECT_NAME FROM USER_OBJECTS
|
||||
WHERE OBJECT_NAME = ?", undef, $trigger_name);
|
||||
if(@$exist_trigger) {
|
||||
$self->do("DROP TRIGGER $trigger_name");
|
||||
}
|
||||
}
|
||||
|
||||
package Bugzilla::DB::Oracle::st;
|
||||
use base qw(DBI::st);
|
||||
|
||||
sub fetchrow_arrayref {
|
||||
my $self = shift;
|
||||
my $ref = $self->SUPER::fetchrow_arrayref(@_);
|
||||
return undef if !defined $ref;
|
||||
Bugzilla::DB::Oracle::_fix_arrayref($ref);
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub fetchrow_array {
|
||||
my $self = shift;
|
||||
if ( wantarray ) {
|
||||
my @row = $self->SUPER::fetchrow_array(@_);
|
||||
Bugzilla::DB::Oracle::_fix_arrayref(\@row);
|
||||
return @row;
|
||||
} else {
|
||||
my $row = $self->SUPER::fetchrow_array(@_);
|
||||
$row = Bugzilla::DB::Oracle::_fix_empty($row) if defined $row;
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
sub fetchrow_hashref {
|
||||
my $self = shift;
|
||||
my $ref = $self->SUPER::fetchrow_hashref(@_);
|
||||
return undef if !defined $ref;
|
||||
Bugzilla::DB::Oracle::_fix_hashref($ref);
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub fetchall_arrayref {
|
||||
my $self = shift;
|
||||
my $ref = $self->SUPER::fetchall_arrayref(@_);
|
||||
return undef if !defined $ref;
|
||||
foreach my $row (@$ref) {
|
||||
if (ref($row) eq 'ARRAY') {
|
||||
Bugzilla::DB::Oracle::_fix_arrayref($row);
|
||||
}
|
||||
elsif (ref($row) eq 'HASH') {
|
||||
Bugzilla::DB::Oracle::_fix_hashref($row);
|
||||
}
|
||||
}
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub fetchall_hashref {
|
||||
my $self = shift;
|
||||
my $ref = $self->SUPER::fetchall_hashref(@_);
|
||||
return undef if !defined $ref;
|
||||
foreach my $row (values %$ref) {
|
||||
Bugzilla::DB::Oracle::_fix_hashref($row);
|
||||
}
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub fetch {
|
||||
my $self = shift;
|
||||
my $row = $self->SUPER::fetch(@_);
|
||||
if ($row) {
|
||||
Bugzilla::DB::Oracle::_fix_arrayref($row);
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
1;
|
||||
@@ -1,342 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Dave Miller <davem00@aol.com>
|
||||
# Gayathri Swaminath <gayathrik00@aol.com>
|
||||
# Jeroen Ruigrok van der Werven <asmodai@wxs.nl>
|
||||
# Dave Lawrence <dkl@redhat.com>
|
||||
# Tomas Kopal <Tomas.Kopal@altap.cz>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
# Lance Larsh <lance.larsh@oracle.com>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::DB::Pg - Bugzilla database compatibility layer for PostgreSQL
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module overrides methods of the Bugzilla::DB module with PostgreSQL
|
||||
specific implementation. It is instantiated by the Bugzilla::DB module
|
||||
and should never be used directly.
|
||||
|
||||
For interface details see L<Bugzilla::DB> and L<DBI>.
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::DB::Pg;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Error;
|
||||
use DBD::Pg;
|
||||
|
||||
# This module extends the DB interface via inheritance
|
||||
use base qw(Bugzilla::DB);
|
||||
|
||||
use constant BLOB_TYPE => { pg_type => DBD::Pg::PG_BYTEA };
|
||||
|
||||
sub new {
|
||||
my ($class, $params) = @_;
|
||||
my ($user, $pass, $host, $dbname, $port) =
|
||||
@$params{qw(db_user db_pass db_host db_name db_port)};
|
||||
|
||||
# The default database name for PostgreSQL. We have
|
||||
# to connect to SOME database, even if we have
|
||||
# no $dbname parameter.
|
||||
$dbname ||= 'template1';
|
||||
|
||||
# construct the DSN from the parameters we got
|
||||
my $dsn = "dbi:Pg:dbname=$dbname";
|
||||
$dsn .= ";host=$host" if $host;
|
||||
$dsn .= ";port=$port" if $port;
|
||||
|
||||
# This stops Pg from printing out lots of "NOTICE" messages when
|
||||
# creating tables.
|
||||
$dsn .= ";options='-c client_min_messages=warning'";
|
||||
|
||||
my $attrs = { pg_enable_utf8 => Bugzilla->params->{'utf8'} };
|
||||
|
||||
my $self = $class->db_new({ dsn => $dsn, user => $user,
|
||||
pass => $pass, attrs => $attrs });
|
||||
|
||||
# all class local variables stored in DBI derived class needs to have
|
||||
# a prefix 'private_'. See DBI documentation.
|
||||
$self->{private_bz_tables_locked} = "";
|
||||
# Needed by TheSchwartz
|
||||
$self->{private_bz_dsn} = $dsn;
|
||||
|
||||
bless ($self, $class);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
# if last_insert_id is supported on PostgreSQL by lowest DBI/DBD version
|
||||
# supported by Bugzilla, this implementation can be removed.
|
||||
sub bz_last_key {
|
||||
my ($self, $table, $column) = @_;
|
||||
|
||||
my $seq = $table . "_" . $column . "_seq";
|
||||
my ($last_insert_id) = $self->selectrow_array("SELECT CURRVAL('$seq')");
|
||||
|
||||
return $last_insert_id;
|
||||
}
|
||||
|
||||
sub sql_group_concat {
|
||||
my ($self, $text, $separator, $sort) = @_;
|
||||
$sort = 1 if !defined $sort;
|
||||
$separator = $self->quote(', ') if !defined $separator;
|
||||
my $sql = "array_accum($text)";
|
||||
if ($sort) {
|
||||
$sql = "array_sort($sql)";
|
||||
}
|
||||
return "array_to_string($sql, $separator)";
|
||||
}
|
||||
|
||||
sub sql_istring {
|
||||
my ($self, $string) = @_;
|
||||
|
||||
return "LOWER(${string}::text)";
|
||||
}
|
||||
|
||||
sub sql_position {
|
||||
my ($self, $fragment, $text) = @_;
|
||||
|
||||
return "POSITION($fragment IN ${text}::text)";
|
||||
}
|
||||
|
||||
sub sql_regexp {
|
||||
my ($self, $expr, $pattern, $nocheck, $real_pattern) = @_;
|
||||
$real_pattern ||= $pattern;
|
||||
|
||||
$self->bz_check_regexp($real_pattern) if !$nocheck;
|
||||
|
||||
return "${expr}::text ~* $pattern";
|
||||
}
|
||||
|
||||
sub sql_not_regexp {
|
||||
my ($self, $expr, $pattern, $nocheck, $real_pattern) = @_;
|
||||
$real_pattern ||= $pattern;
|
||||
|
||||
$self->bz_check_regexp($real_pattern) if !$nocheck;
|
||||
|
||||
return "${expr}::text !~* $pattern"
|
||||
}
|
||||
|
||||
sub sql_limit {
|
||||
my ($self, $limit, $offset) = @_;
|
||||
|
||||
if (defined($offset)) {
|
||||
return "LIMIT $limit OFFSET $offset";
|
||||
} else {
|
||||
return "LIMIT $limit";
|
||||
}
|
||||
}
|
||||
|
||||
sub sql_from_days {
|
||||
my ($self, $days) = @_;
|
||||
|
||||
return "TO_TIMESTAMP('$days', 'J')::date";
|
||||
}
|
||||
|
||||
sub sql_to_days {
|
||||
my ($self, $date) = @_;
|
||||
|
||||
return "TO_CHAR(${date}::date, 'J')::int";
|
||||
}
|
||||
|
||||
sub sql_date_format {
|
||||
my ($self, $date, $format) = @_;
|
||||
|
||||
$format = "%Y.%m.%d %H:%i:%s" if !$format;
|
||||
|
||||
$format =~ s/\%Y/YYYY/g;
|
||||
$format =~ s/\%y/YY/g;
|
||||
$format =~ s/\%m/MM/g;
|
||||
$format =~ s/\%d/DD/g;
|
||||
$format =~ s/\%a/Dy/g;
|
||||
$format =~ s/\%H/HH24/g;
|
||||
$format =~ s/\%i/MI/g;
|
||||
$format =~ s/\%s/SS/g;
|
||||
|
||||
return "TO_CHAR($date, " . $self->quote($format) . ")";
|
||||
}
|
||||
|
||||
sub sql_interval {
|
||||
my ($self, $interval, $units) = @_;
|
||||
|
||||
return "$interval * INTERVAL '1 $units'";
|
||||
}
|
||||
|
||||
sub sql_string_concat {
|
||||
my ($self, @params) = @_;
|
||||
|
||||
# Postgres 7.3 does not support concatenating of different types, so we
|
||||
# need to cast both parameters to text. Version 7.4 seems to handle this
|
||||
# properly, so when we stop support 7.3, this can be removed.
|
||||
return '(CAST(' . join(' AS text) || CAST(', @params) . ' AS text))';
|
||||
}
|
||||
|
||||
# Tell us whether or not a particular sequence exists in the DB.
|
||||
sub bz_sequence_exists {
|
||||
my ($self, $seq_name) = @_;
|
||||
my $exists = $self->selectrow_array(
|
||||
'SELECT 1 FROM pg_statio_user_sequences WHERE relname = ?',
|
||||
undef, $seq_name);
|
||||
return $exists || 0;
|
||||
}
|
||||
|
||||
sub bz_explain {
|
||||
my ($self, $sql) = @_;
|
||||
my $explain = $self->selectcol_arrayref("EXPLAIN ANALYZE $sql");
|
||||
return join("\n", @$explain);
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# Custom Database Setup
|
||||
#####################################################################
|
||||
|
||||
sub bz_check_server_version {
|
||||
my $self = shift;
|
||||
my ($db) = @_;
|
||||
my $server_version = $self->SUPER::bz_check_server_version(@_);
|
||||
my ($major_version) = $server_version =~ /^(\d+)/;
|
||||
# Pg 9 requires DBD::Pg 2.17.2 in order to properly read bytea values.
|
||||
if ($major_version >= 9) {
|
||||
local $db->{dbd}->{version} = '2.17.2';
|
||||
local $db->{name} = $db->{name} . ' 9+';
|
||||
Bugzilla::DB::_bz_check_dbd(@_);
|
||||
}
|
||||
}
|
||||
|
||||
sub bz_setup_database {
|
||||
my $self = shift;
|
||||
$self->SUPER::bz_setup_database(@_);
|
||||
|
||||
# Custom Functions
|
||||
my $function = 'array_accum';
|
||||
my $array_accum = $self->selectrow_array(
|
||||
'SELECT 1 FROM pg_proc WHERE proname = ?', undef, $function);
|
||||
if (!$array_accum) {
|
||||
print "Creating function $function...\n";
|
||||
$self->do("CREATE AGGREGATE array_accum (
|
||||
SFUNC = array_append,
|
||||
BASETYPE = anyelement,
|
||||
STYPE = anyarray,
|
||||
INITCOND = '{}'
|
||||
)");
|
||||
}
|
||||
|
||||
$self->do(<<'END');
|
||||
CREATE OR REPLACE FUNCTION array_sort(ANYARRAY)
|
||||
RETURNS ANYARRAY LANGUAGE SQL
|
||||
IMMUTABLE STRICT
|
||||
AS $$
|
||||
SELECT ARRAY(
|
||||
SELECT $1[s.i] AS each_item
|
||||
FROM
|
||||
generate_series(array_lower($1,1), array_upper($1,1)) AS s(i)
|
||||
ORDER BY each_item
|
||||
);
|
||||
$$;
|
||||
END
|
||||
|
||||
# PostgreSQL doesn't like having *any* index on the thetext
|
||||
# field, because it can't have index data longer than 2770
|
||||
# characters on that field.
|
||||
$self->bz_drop_index('longdescs', 'longdescs_thetext_idx');
|
||||
# Same for all the comments fields in the fulltext table.
|
||||
$self->bz_drop_index('bugs_fulltext', 'bugs_fulltext_comments_idx');
|
||||
$self->bz_drop_index('bugs_fulltext',
|
||||
'bugs_fulltext_comments_noprivate_idx');
|
||||
|
||||
# PostgreSQL also wants an index for calling LOWER on
|
||||
# login_name, which we do with sql_istrcmp all over the place.
|
||||
$self->bz_add_index('profiles', 'profiles_login_name_lower_idx',
|
||||
{FIELDS => ['LOWER(login_name)'], TYPE => 'UNIQUE'});
|
||||
|
||||
# Now that Bugzilla::Object uses sql_istrcmp, other tables
|
||||
# also need a LOWER() index.
|
||||
_fix_case_differences('fielddefs', 'name');
|
||||
$self->bz_add_index('fielddefs', 'fielddefs_name_lower_idx',
|
||||
{FIELDS => ['LOWER(name)'], TYPE => 'UNIQUE'});
|
||||
_fix_case_differences('keyworddefs', 'name');
|
||||
$self->bz_add_index('keyworddefs', 'keyworddefs_name_lower_idx',
|
||||
{FIELDS => ['LOWER(name)'], TYPE => 'UNIQUE'});
|
||||
_fix_case_differences('products', 'name');
|
||||
$self->bz_add_index('products', 'products_name_lower_idx',
|
||||
{FIELDS => ['LOWER(name)'], TYPE => 'UNIQUE'});
|
||||
|
||||
# bz_rename_column didn't correctly rename the sequence.
|
||||
if ($self->bz_column_info('fielddefs', 'id')
|
||||
&& $self->bz_sequence_exists('fielddefs_fieldid_seq'))
|
||||
{
|
||||
print "Fixing fielddefs_fieldid_seq sequence...\n";
|
||||
$self->do("ALTER TABLE fielddefs_fieldid_seq RENAME TO fielddefs_id_seq");
|
||||
$self->do("ALTER TABLE fielddefs ALTER COLUMN id
|
||||
SET DEFAULT NEXTVAL('fielddefs_id_seq')");
|
||||
}
|
||||
}
|
||||
|
||||
# Renames things that differ only in case.
|
||||
sub _fix_case_differences {
|
||||
my ($table, $field) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $duplicates = $dbh->selectcol_arrayref(
|
||||
"SELECT DISTINCT LOWER($field) FROM $table
|
||||
GROUP BY LOWER($field) HAVING COUNT(LOWER($field)) > 1");
|
||||
|
||||
foreach my $name (@$duplicates) {
|
||||
my $dups = $dbh->selectcol_arrayref(
|
||||
"SELECT $field FROM $table WHERE LOWER($field) = ?",
|
||||
undef, $name);
|
||||
my $primary = shift @$dups;
|
||||
foreach my $dup (@$dups) {
|
||||
my $new_name = "${dup}_";
|
||||
# Make sure the new name isn't *also* a duplicate.
|
||||
while (1) {
|
||||
last if (!$dbh->selectrow_array(
|
||||
"SELECT 1 FROM $table WHERE LOWER($field) = ?",
|
||||
undef, lc($new_name)));
|
||||
$new_name .= "_";
|
||||
}
|
||||
print "$table '$primary' and '$dup' have names that differ",
|
||||
" only in case.\nRenaming '$dup' to '$new_name'...\n";
|
||||
$dbh->do("UPDATE $table SET $field = ? WHERE $field = ?",
|
||||
undef, $new_name, $dup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# Custom Schema Information Functions
|
||||
#####################################################################
|
||||
|
||||
# Pg includes the PostgreSQL system tables in table_list_real, so
|
||||
# we need to remove those.
|
||||
sub bz_table_list_real {
|
||||
my $self = shift;
|
||||
|
||||
my @full_table_list = $self->SUPER::bz_table_list_real(@_);
|
||||
# All PostgreSQL system tables start with "pg_" or "sql_"
|
||||
my @table_list = grep(!/(^pg_)|(^sql_)/, @full_table_list);
|
||||
return @table_list;
|
||||
}
|
||||
|
||||
1;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,399 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Andrew Dunstan <andrew@dunslane.net>,
|
||||
# Edward J. Sabol <edwardjsabol@iname.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::DB::Schema::Mysql;
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# DB::Schema implementation for MySQL
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
use strict;
|
||||
use Bugzilla::Error;
|
||||
|
||||
use base qw(Bugzilla::DB::Schema);
|
||||
|
||||
# This is for column_info_to_column, to know when a tinyint is a
|
||||
# boolean and when it's really a tinyint. This only has to be accurate
|
||||
# up to and through 2.19.3, because that's the only time we need
|
||||
# column_info_to_column.
|
||||
#
|
||||
# This is basically a hash of tables/columns, with one entry for each column
|
||||
# that should be interpreted as a BOOLEAN instead of as an INT1 when
|
||||
# reading in the Schema from the disk. The values are discarded; I just
|
||||
# used "1" for simplicity.
|
||||
#
|
||||
# THIS CONSTANT IS ONLY USED FOR UPGRADES FROM 2.18 OR EARLIER. DON'T
|
||||
# UPDATE IT TO MODERN COLUMN NAMES OR DEFINITIONS.
|
||||
use constant BOOLEAN_MAP => {
|
||||
bugs => {everconfirmed => 1, reporter_accessible => 1,
|
||||
cclist_accessible => 1, qacontact_accessible => 1,
|
||||
assignee_accessible => 1},
|
||||
longdescs => {isprivate => 1, already_wrapped => 1},
|
||||
attachments => {ispatch => 1, isobsolete => 1, isprivate => 1},
|
||||
flags => {is_active => 1},
|
||||
flagtypes => {is_active => 1, is_requestable => 1,
|
||||
is_requesteeble => 1, is_multiplicable => 1},
|
||||
fielddefs => {mailhead => 1, obsolete => 1},
|
||||
bug_status => {isactive => 1},
|
||||
resolution => {isactive => 1},
|
||||
bug_severity => {isactive => 1},
|
||||
priority => {isactive => 1},
|
||||
rep_platform => {isactive => 1},
|
||||
op_sys => {isactive => 1},
|
||||
profiles => {mybugslink => 1, newemailtech => 1},
|
||||
namedqueries => {linkinfooter => 1, watchfordiffs => 1},
|
||||
groups => {isbuggroup => 1, isactive => 1},
|
||||
group_control_map => {entry => 1, membercontrol => 1, othercontrol => 1,
|
||||
canedit => 1},
|
||||
group_group_map => {isbless => 1},
|
||||
user_group_map => {isbless => 1, isderived => 1},
|
||||
products => {disallownew => 1},
|
||||
series => {public => 1},
|
||||
whine_queries => {onemailperbug => 1},
|
||||
quips => {approved => 1},
|
||||
setting => {is_enabled => 1}
|
||||
};
|
||||
|
||||
# Maps the db_specific hash backwards, for use in column_info_to_column.
|
||||
use constant REVERSE_MAPPING => {
|
||||
# Boolean and the SERIAL fields are handled in column_info_to_column,
|
||||
# and so don't have an entry here.
|
||||
TINYINT => 'INT1',
|
||||
SMALLINT => 'INT2',
|
||||
MEDIUMINT => 'INT3',
|
||||
INTEGER => 'INT4',
|
||||
|
||||
# All the other types have the same name in their abstract version
|
||||
# as in their db-specific version, so no reverse mapping is needed.
|
||||
};
|
||||
|
||||
use constant MYISAM_TABLES => qw(bugs_fulltext);
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
sub _initialize {
|
||||
|
||||
my $self = shift;
|
||||
|
||||
$self = $self->SUPER::_initialize(@_);
|
||||
|
||||
$self->{db_specific} = {
|
||||
|
||||
BOOLEAN => 'tinyint',
|
||||
FALSE => '0',
|
||||
TRUE => '1',
|
||||
|
||||
INT1 => 'tinyint',
|
||||
INT2 => 'smallint',
|
||||
INT3 => 'mediumint',
|
||||
INT4 => 'integer',
|
||||
|
||||
SMALLSERIAL => 'smallint auto_increment',
|
||||
MEDIUMSERIAL => 'mediumint auto_increment',
|
||||
INTSERIAL => 'integer auto_increment',
|
||||
|
||||
TINYTEXT => 'tinytext',
|
||||
MEDIUMTEXT => 'mediumtext',
|
||||
LONGTEXT => 'mediumtext',
|
||||
|
||||
LONGBLOB => 'longblob',
|
||||
|
||||
DATETIME => 'datetime',
|
||||
|
||||
};
|
||||
|
||||
$self->_adjust_schema;
|
||||
|
||||
return $self;
|
||||
|
||||
} #eosub--_initialize
|
||||
#------------------------------------------------------------------------------
|
||||
sub _get_create_table_ddl {
|
||||
# Extend superclass method to specify the MYISAM storage engine.
|
||||
# Returns a "create table" SQL statement.
|
||||
|
||||
my($self, $table) = @_;
|
||||
|
||||
my $charset = Bugzilla->dbh->bz_db_is_utf8 ? "CHARACTER SET utf8" : '';
|
||||
my $type = grep($_ eq $table, MYISAM_TABLES) ? 'MYISAM' : 'InnoDB';
|
||||
return($self->SUPER::_get_create_table_ddl($table)
|
||||
. " ENGINE = $type $charset");
|
||||
|
||||
} #eosub--_get_create_table_ddl
|
||||
#------------------------------------------------------------------------------
|
||||
sub _get_create_index_ddl {
|
||||
# Extend superclass method to create FULLTEXT indexes on text fields.
|
||||
# Returns a "create index" SQL statement.
|
||||
|
||||
my($self, $table_name, $index_name, $index_fields, $index_type) = @_;
|
||||
|
||||
my $sql = "CREATE ";
|
||||
$sql .= "$index_type " if ($index_type eq 'UNIQUE'
|
||||
|| $index_type eq 'FULLTEXT');
|
||||
$sql .= "INDEX \`$index_name\` ON $table_name \(" .
|
||||
join(", ", @$index_fields) . "\)";
|
||||
|
||||
return($sql);
|
||||
|
||||
} #eosub--_get_create_index_ddl
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
sub get_create_database_sql {
|
||||
my ($self, $name) = @_;
|
||||
# We only create as utf8 if we have no params (meaning we're doing
|
||||
# a new installation) or if the utf8 param is on.
|
||||
my $create_utf8 = Bugzilla->params->{'utf8'}
|
||||
|| !defined Bugzilla->params->{'utf8'};
|
||||
my $charset = $create_utf8 ? "CHARACTER SET utf8" : '';
|
||||
return ("CREATE DATABASE $name $charset");
|
||||
}
|
||||
|
||||
# MySQL has a simpler ALTER TABLE syntax than ANSI.
|
||||
sub get_alter_column_ddl {
|
||||
my ($self, $table, $column, $new_def, $set_nulls_to) = @_;
|
||||
my $old_def = $self->get_column($table, $column);
|
||||
my %new_def_copy = %$new_def;
|
||||
if ($old_def->{PRIMARYKEY} && $new_def->{PRIMARYKEY}) {
|
||||
# If a column stays a primary key do NOT specify PRIMARY KEY in the
|
||||
# ALTER TABLE statement. This avoids a MySQL error that two primary
|
||||
# keys are not allowed.
|
||||
delete $new_def_copy{PRIMARYKEY};
|
||||
}
|
||||
|
||||
my @statements;
|
||||
|
||||
push(@statements, "UPDATE $table SET $column = $set_nulls_to
|
||||
WHERE $column IS NULL") if defined $set_nulls_to;
|
||||
|
||||
# Calling SET DEFAULT or DROP DEFAULT is *way* faster than calling
|
||||
# CHANGE COLUMN, so just do that if we're just changing the default.
|
||||
my %old_defaultless = %$old_def;
|
||||
my %new_defaultless = %$new_def;
|
||||
delete $old_defaultless{DEFAULT};
|
||||
delete $new_defaultless{DEFAULT};
|
||||
if (!$self->columns_equal($old_def, $new_def)
|
||||
&& $self->columns_equal(\%new_defaultless, \%old_defaultless))
|
||||
{
|
||||
if (!defined $new_def->{DEFAULT}) {
|
||||
push(@statements,
|
||||
"ALTER TABLE $table ALTER COLUMN $column DROP DEFAULT");
|
||||
}
|
||||
else {
|
||||
push(@statements, "ALTER TABLE $table ALTER COLUMN $column
|
||||
SET DEFAULT " . $new_def->{DEFAULT});
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $new_ddl = $self->get_type_ddl(\%new_def_copy);
|
||||
push(@statements, "ALTER TABLE $table CHANGE COLUMN
|
||||
$column $column $new_ddl");
|
||||
}
|
||||
|
||||
if ($old_def->{PRIMARYKEY} && !$new_def->{PRIMARYKEY}) {
|
||||
# Dropping a PRIMARY KEY needs an explicit DROP PRIMARY KEY
|
||||
push(@statements, "ALTER TABLE $table DROP PRIMARY KEY");
|
||||
}
|
||||
|
||||
return @statements;
|
||||
}
|
||||
|
||||
sub get_drop_fk_sql {
|
||||
my ($self, $table, $column, $references) = @_;
|
||||
my $fk_name = $self->_get_fk_name($table, $column, $references);
|
||||
my @sql = ("ALTER TABLE $table DROP FOREIGN KEY $fk_name");
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# MySQL requires, and will create, an index on any column with
|
||||
# an FK. It will name it after the fk, which we never do.
|
||||
# So if there's an index named after the fk, we also have to delete it.
|
||||
if ($dbh->bz_index_info_real($table, $fk_name)) {
|
||||
push(@sql, $self->get_drop_index_ddl($table, $fk_name));
|
||||
}
|
||||
|
||||
return @sql;
|
||||
}
|
||||
|
||||
sub get_drop_index_ddl {
|
||||
my ($self, $table, $name) = @_;
|
||||
return ("DROP INDEX \`$name\` ON $table");
|
||||
}
|
||||
|
||||
# A special function for MySQL, for renaming a lot of indexes.
|
||||
# Index renames is a hash, where the key is a string - the
|
||||
# old names of the index, and the value is a hash - the index
|
||||
# definition that we're renaming to, with an extra key of "NAME"
|
||||
# that contains the new index name.
|
||||
# The indexes in %indexes must be in hashref format.
|
||||
sub get_rename_indexes_ddl {
|
||||
my ($self, $table, %indexes) = @_;
|
||||
my @keys = keys %indexes or return ();
|
||||
|
||||
my $sql = "ALTER TABLE $table ";
|
||||
|
||||
foreach my $old_name (@keys) {
|
||||
my $name = $indexes{$old_name}->{NAME};
|
||||
my $type = $indexes{$old_name}->{TYPE};
|
||||
$type ||= 'INDEX';
|
||||
my $fields = join(',', @{$indexes{$old_name}->{FIELDS}});
|
||||
# $old_name needs to be escaped, sometimes, because it was
|
||||
# a reserved word.
|
||||
$old_name = '`' . $old_name . '`';
|
||||
$sql .= " ADD $type $name ($fields), DROP INDEX $old_name,";
|
||||
}
|
||||
# Remove the last comma.
|
||||
chop($sql);
|
||||
return ($sql);
|
||||
}
|
||||
|
||||
sub get_set_serial_sql {
|
||||
my ($self, $table, $column, $value) = @_;
|
||||
return ("ALTER TABLE $table AUTO_INCREMENT = $value");
|
||||
}
|
||||
|
||||
# Converts a DBI column_info output to an abstract column definition.
|
||||
# Expects to only be called by Bugzila::DB::Mysql::_bz_build_schema_from_disk,
|
||||
# although there's a chance that it will also work properly if called
|
||||
# elsewhere.
|
||||
sub column_info_to_column {
|
||||
my ($self, $column_info) = @_;
|
||||
|
||||
# Unfortunately, we have to break Schema's normal "no database"
|
||||
# barrier a few times in this function.
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $table = $column_info->{TABLE_NAME};
|
||||
my $col_name = $column_info->{COLUMN_NAME};
|
||||
|
||||
my $column = {};
|
||||
|
||||
($column->{NOTNULL} = 1) if $column_info->{NULLABLE} == 0;
|
||||
|
||||
if ($column_info->{mysql_is_pri_key}) {
|
||||
# In MySQL, if a table has no PK, but it has a UNIQUE index,
|
||||
# that index will show up as the PK. So we have to eliminate
|
||||
# that possibility.
|
||||
# Unfortunately, the only way to definitely solve this is
|
||||
# to break Schema's standard of not touching the live database
|
||||
# and check if the index called PRIMARY is on that field.
|
||||
my $pri_index = $dbh->bz_index_info_real($table, 'PRIMARY');
|
||||
if ( $pri_index && grep($_ eq $col_name, @{$pri_index->{FIELDS}}) ) {
|
||||
$column->{PRIMARYKEY} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# MySQL frequently defines a default for a field even when we
|
||||
# didn't explicitly set one. So we have to have some special
|
||||
# hacks to determine whether or not we should actually put
|
||||
# a default in the abstract schema for this field.
|
||||
if (defined $column_info->{COLUMN_DEF}) {
|
||||
# The defaults that MySQL inputs automatically are usually
|
||||
# something that would be considered "false" by perl, either
|
||||
# a 0 or an empty string. (Except for datetime and decimal
|
||||
# fields, which have their own special auto-defaults.)
|
||||
#
|
||||
# Here's how we handle this: If it exists in the schema
|
||||
# without a default, then we don't use the default. If it
|
||||
# doesn't exist in the schema, then we're either going to
|
||||
# be dropping it soon, or it's a custom end-user column, in which
|
||||
# case having a bogus default won't harm anything.
|
||||
my $schema_column = $self->get_column($table, $col_name);
|
||||
unless ( (!$column_info->{COLUMN_DEF}
|
||||
|| $column_info->{COLUMN_DEF} eq '0000-00-00 00:00:00'
|
||||
|| $column_info->{COLUMN_DEF} eq '0.00')
|
||||
&& $schema_column
|
||||
&& !exists $schema_column->{DEFAULT}) {
|
||||
|
||||
my $default = $column_info->{COLUMN_DEF};
|
||||
# Schema uses '0' for the defaults for decimal fields.
|
||||
$default = 0 if $default =~ /^0\.0+$/;
|
||||
# If we're not a number, we're a string and need to be
|
||||
# quoted.
|
||||
$default = $dbh->quote($default) if !($default =~ /^(-)?(\d+)(.\d+)?$/);
|
||||
$column->{DEFAULT} = $default;
|
||||
}
|
||||
}
|
||||
|
||||
my $type = $column_info->{TYPE_NAME};
|
||||
|
||||
# Certain types of columns need the size/precision appended.
|
||||
if ($type =~ /CHAR$/ || $type eq 'DECIMAL') {
|
||||
# This is nicely lowercase and has the size/precision appended.
|
||||
$type = $column_info->{mysql_type_name};
|
||||
}
|
||||
|
||||
# If we're a tinyint, we could be either a BOOLEAN or an INT1.
|
||||
# Only the BOOLEAN_MAP knows the difference.
|
||||
elsif ($type eq 'TINYINT' && exists BOOLEAN_MAP->{$table}
|
||||
&& exists BOOLEAN_MAP->{$table}->{$col_name}) {
|
||||
$type = 'BOOLEAN';
|
||||
if (exists $column->{DEFAULT}) {
|
||||
$column->{DEFAULT} = $column->{DEFAULT} ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
}
|
||||
|
||||
# We also need to check if we're an auto_increment field.
|
||||
elsif ($type =~ /INT/) {
|
||||
# Unfortunately, the only way to do this in DBI is to query the
|
||||
# database, so we have to break the rule here that Schema normally
|
||||
# doesn't touch the live DB.
|
||||
my $ref_sth = $dbh->prepare(
|
||||
"SELECT $col_name FROM $table LIMIT 1");
|
||||
$ref_sth->execute;
|
||||
if ($ref_sth->{mysql_is_auto_increment}->[0]) {
|
||||
if ($type eq 'MEDIUMINT') {
|
||||
$type = 'MEDIUMSERIAL';
|
||||
}
|
||||
elsif ($type eq 'SMALLINT') {
|
||||
$type = 'SMALLSERIAL';
|
||||
}
|
||||
else {
|
||||
$type = 'INTSERIAL';
|
||||
}
|
||||
}
|
||||
$ref_sth->finish;
|
||||
|
||||
}
|
||||
|
||||
# For all other db-specific types, check if they exist in
|
||||
# REVERSE_MAPPING and use the type found there.
|
||||
if (exists REVERSE_MAPPING->{$type}) {
|
||||
$type = REVERSE_MAPPING->{$type};
|
||||
}
|
||||
|
||||
$column->{TYPE} = $type;
|
||||
|
||||
#print "$table.$col_name: " . Data::Dumper->Dump([$column]) . "\n";
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
sub get_rename_column_ddl {
|
||||
my ($self, $table, $old_name, $new_name) = @_;
|
||||
my $def = $self->get_type_ddl($self->get_column($table, $old_name));
|
||||
# MySQL doesn't like having the PRIMARY KEY statement in a rename.
|
||||
$def =~ s/PRIMARY KEY//i;
|
||||
return ("ALTER TABLE $table CHANGE COLUMN $old_name $new_name $def");
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,439 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Oracle Corporation.
|
||||
# Portions created by Oracle are Copyright (C) 2007 Oracle Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Lance Larsh <lance.larsh@oracle.com>
|
||||
# Xiaoou Wu <xiaoou.wu@oracle.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::DB::Schema::Oracle;
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# DB::Schema implementation for Oracle
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Bugzilla::DB::Schema);
|
||||
use Carp qw(confess);
|
||||
use Bugzilla::Util;
|
||||
|
||||
use constant ADD_COLUMN => 'ADD';
|
||||
use constant MULTIPLE_FKS_IN_ALTER => 0;
|
||||
# Whether this is true or not, this is what it needs to be in order for
|
||||
# hash_identifier to maintain backwards compatibility with versions before
|
||||
# 3.2rc2.
|
||||
use constant MAX_IDENTIFIER_LEN => 27;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
sub _initialize {
|
||||
|
||||
my $self = shift;
|
||||
|
||||
$self = $self->SUPER::_initialize(@_);
|
||||
|
||||
$self->{db_specific} = {
|
||||
|
||||
BOOLEAN => 'integer',
|
||||
FALSE => '0',
|
||||
TRUE => '1',
|
||||
|
||||
INT1 => 'integer',
|
||||
INT2 => 'integer',
|
||||
INT3 => 'integer',
|
||||
INT4 => 'integer',
|
||||
|
||||
SMALLSERIAL => 'integer',
|
||||
MEDIUMSERIAL => 'integer',
|
||||
INTSERIAL => 'integer',
|
||||
|
||||
TINYTEXT => 'varchar(255)',
|
||||
MEDIUMTEXT => 'varchar(4000)',
|
||||
LONGTEXT => 'clob',
|
||||
|
||||
LONGBLOB => 'blob',
|
||||
|
||||
DATETIME => 'date',
|
||||
|
||||
};
|
||||
|
||||
$self->_adjust_schema;
|
||||
|
||||
return $self;
|
||||
|
||||
} #eosub--_initialize
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
sub get_table_ddl {
|
||||
my $self = shift;
|
||||
my $table = shift;
|
||||
unshift @_, $table;
|
||||
my @ddl = $self->SUPER::get_table_ddl(@_);
|
||||
|
||||
my @fields = @{ $self->{abstract_schema}{$table}{FIELDS} || [] };
|
||||
while (@fields) {
|
||||
my $field_name = shift @fields;
|
||||
my $field_info = shift @fields;
|
||||
# Create triggers to deal with empty string.
|
||||
if ( $field_info->{TYPE} =~ /varchar|TEXT/i
|
||||
&& $field_info->{NOTNULL} ) {
|
||||
push (@ddl, _get_notnull_trigger_ddl($table, $field_name));
|
||||
}
|
||||
# Create sequences and triggers to emulate SERIAL datatypes.
|
||||
if ( $field_info->{TYPE} =~ /SERIAL/i ) {
|
||||
push (@ddl, $self->_get_create_seq_ddl($table, $field_name));
|
||||
}
|
||||
}
|
||||
return @ddl;
|
||||
|
||||
} #eosub--get_table_ddl
|
||||
|
||||
# Extend superclass method to create Oracle Text indexes if index type
|
||||
# is FULLTEXT from schema. Returns a "create index" SQL statement.
|
||||
sub _get_create_index_ddl {
|
||||
|
||||
my ($self, $table_name, $index_name, $index_fields, $index_type) = @_;
|
||||
$index_name = "idx_" . $self->_hash_identifier($index_name);
|
||||
if ($index_type eq 'FULLTEXT') {
|
||||
my $sql = "CREATE INDEX $index_name ON $table_name ("
|
||||
. join(',',@$index_fields)
|
||||
. ") INDEXTYPE IS CTXSYS.CONTEXT "
|
||||
. " PARAMETERS('LEXER BZ_LEX SYNC(ON COMMIT)')" ;
|
||||
return $sql;
|
||||
}
|
||||
|
||||
return($self->SUPER::_get_create_index_ddl($table_name, $index_name,
|
||||
$index_fields, $index_type));
|
||||
|
||||
}
|
||||
|
||||
sub get_drop_index_ddl {
|
||||
my $self = shift;
|
||||
my ($table, $name) = @_;
|
||||
|
||||
$name = 'idx_' . $self->_hash_identifier($name);
|
||||
return $self->SUPER::get_drop_index_ddl($table, $name);
|
||||
}
|
||||
|
||||
# Oracle supports the use of FOREIGN KEY integrity constraints
|
||||
# to define the referential integrity actions, including:
|
||||
# - Update and delete No Action (default)
|
||||
# - Delete CASCADE
|
||||
# - Delete SET NULL
|
||||
sub get_fk_ddl {
|
||||
my $self = shift;
|
||||
my $ddl = $self->SUPER::get_fk_ddl(@_);
|
||||
|
||||
# iThe Bugzilla Oracle driver implements UPDATE via a trigger.
|
||||
$ddl =~ s/ON UPDATE \S+//i;
|
||||
# RESTRICT is the default for DELETE on Oracle and may not be specified.
|
||||
$ddl =~ s/ON DELETE RESTRICT//i;
|
||||
|
||||
return $ddl;
|
||||
}
|
||||
|
||||
sub get_add_fks_sql {
|
||||
my $self = shift;
|
||||
my ($table, $column_fks) = @_;
|
||||
my @sql = $self->SUPER::get_add_fks_sql(@_);
|
||||
|
||||
foreach my $column (keys %$column_fks) {
|
||||
my $fk = $column_fks->{$column};
|
||||
next if $fk->{UPDATE} && uc($fk->{UPDATE}) ne 'CASCADE';
|
||||
my $fk_name = $self->_get_fk_name($table, $column, $fk);
|
||||
my $to_column = $fk->{COLUMN};
|
||||
my $to_table = $fk->{TABLE};
|
||||
|
||||
my $trigger = <<END;
|
||||
CREATE OR REPLACE TRIGGER ${fk_name}_UC
|
||||
AFTER UPDATE OF $to_column ON $to_table
|
||||
REFERENCING NEW AS NEW OLD AS OLD
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE $table
|
||||
SET $column = :NEW.$to_column
|
||||
WHERE $column = :OLD.$to_column;
|
||||
END ${fk_name}_UC;
|
||||
END
|
||||
push(@sql, $trigger);
|
||||
}
|
||||
|
||||
return @sql;
|
||||
}
|
||||
|
||||
sub get_drop_fk_sql {
|
||||
my $self = shift;
|
||||
my ($table, $column, $references) = @_;
|
||||
my $fk_name = $self->_get_fk_name(@_);
|
||||
my @sql;
|
||||
if (!$references->{UPDATE} || $references->{UPDATE} =~ /CASCADE/i) {
|
||||
push(@sql, "DROP TRIGGER ${fk_name}_uc");
|
||||
}
|
||||
push(@sql, $self->SUPER::get_drop_fk_sql(@_));
|
||||
return @sql;
|
||||
}
|
||||
|
||||
sub _get_fk_name {
|
||||
my ($self, $table, $column, $references) = @_;
|
||||
my $to_table = $references->{TABLE};
|
||||
my $to_column = $references->{COLUMN};
|
||||
my $fk_name = "${table}_${column}_${to_table}_${to_column}";
|
||||
$fk_name = "fk_" . $self->_hash_identifier($fk_name);
|
||||
|
||||
return $fk_name;
|
||||
}
|
||||
|
||||
sub get_alter_column_ddl {
|
||||
my ($self, $table, $column, $new_def, $set_nulls_to) = @_;
|
||||
|
||||
my @statements;
|
||||
my $old_def = $self->get_column_abstract($table, $column);
|
||||
my $specific = $self->{db_specific};
|
||||
|
||||
# If the types have changed, we have to deal with that.
|
||||
if (uc(trim($old_def->{TYPE})) ne uc(trim($new_def->{TYPE}))) {
|
||||
push(@statements, $self->_get_alter_type_sql($table, $column,
|
||||
$new_def, $old_def));
|
||||
}
|
||||
|
||||
my $default = $new_def->{DEFAULT};
|
||||
my $default_old = $old_def->{DEFAULT};
|
||||
|
||||
if (defined $default) {
|
||||
$default = $specific->{$default} if exists $specific->{$default};
|
||||
}
|
||||
# This first condition prevents "uninitialized value" errors.
|
||||
if (!defined $default && !defined $default_old) {
|
||||
# Do Nothing
|
||||
}
|
||||
# If we went from having a default to not having one
|
||||
elsif (!defined $default && defined $default_old) {
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column"
|
||||
. " DEFAULT NULL");
|
||||
}
|
||||
# If we went from no default to a default, or we changed the default.
|
||||
elsif ( (defined $default && !defined $default_old) ||
|
||||
($default ne $default_old) )
|
||||
{
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column "
|
||||
. " DEFAULT $default");
|
||||
}
|
||||
|
||||
# If we went from NULL to NOT NULL.
|
||||
if (!$old_def->{NOTNULL} && $new_def->{NOTNULL}) {
|
||||
my $setdefault;
|
||||
# Handle any fields that were NULL before, if we have a default,
|
||||
$setdefault = $default if defined $default;
|
||||
# But if we have a set_nulls_to, that overrides the DEFAULT
|
||||
# (although nobody would usually specify both a default and
|
||||
# a set_nulls_to.)
|
||||
$setdefault = $set_nulls_to if defined $set_nulls_to;
|
||||
if (defined $setdefault) {
|
||||
push(@statements, "UPDATE $table SET $column = $setdefault"
|
||||
. " WHERE $column IS NULL");
|
||||
}
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column"
|
||||
. " NOT NULL");
|
||||
push (@statements, _get_notnull_trigger_ddl($table, $column))
|
||||
if $old_def->{TYPE} =~ /varchar|text/i
|
||||
&& $new_def->{TYPE} =~ /varchar|text/i;
|
||||
}
|
||||
# If we went from NOT NULL to NULL
|
||||
elsif ($old_def->{NOTNULL} && !$new_def->{NOTNULL}) {
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column"
|
||||
. " NULL");
|
||||
push(@statements, "DROP TRIGGER ${table}_${column}")
|
||||
if $new_def->{TYPE} =~ /varchar|text/i
|
||||
&& $old_def->{TYPE} =~ /varchar|text/i;
|
||||
}
|
||||
|
||||
# If we went from not being a PRIMARY KEY to being a PRIMARY KEY.
|
||||
if (!$old_def->{PRIMARYKEY} && $new_def->{PRIMARYKEY}) {
|
||||
push(@statements, "ALTER TABLE $table ADD PRIMARY KEY ($column)");
|
||||
}
|
||||
# If we went from being a PK to not being a PK
|
||||
elsif ( $old_def->{PRIMARYKEY} && !$new_def->{PRIMARYKEY} ) {
|
||||
push(@statements, "ALTER TABLE $table DROP PRIMARY KEY");
|
||||
}
|
||||
|
||||
return @statements;
|
||||
}
|
||||
|
||||
sub _get_alter_type_sql {
|
||||
my ($self, $table, $column, $new_def, $old_def) = @_;
|
||||
my @statements;
|
||||
|
||||
my $type = $new_def->{TYPE};
|
||||
$type = $self->{db_specific}->{$type}
|
||||
if exists $self->{db_specific}->{$type};
|
||||
|
||||
if ($type =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
|
||||
die("You cannot specify a DEFAULT on a SERIAL-type column.")
|
||||
if $new_def->{DEFAULT};
|
||||
}
|
||||
|
||||
if ( ($old_def->{TYPE} =~ /LONGTEXT/i && $new_def->{TYPE} !~ /LONGTEXT/i)
|
||||
|| ($old_def->{TYPE} !~ /LONGTEXT/i && $new_def->{TYPE} =~ /LONGTEXT/i)
|
||||
) {
|
||||
# LONG to VARCHAR or VARCHAR to LONG is not allowed in Oracle,
|
||||
# just a way to work around.
|
||||
# Determine whether column_temp is already exist.
|
||||
my $dbh=Bugzilla->dbh;
|
||||
my $column_exist = $dbh->selectcol_arrayref(
|
||||
"SELECT CNAME FROM COL WHERE TNAME = UPPER(?) AND
|
||||
CNAME = UPPER(?)", undef,$table,$column . "_temp");
|
||||
if(!@$column_exist) {
|
||||
push(@statements,
|
||||
"ALTER TABLE $table ADD ${column}_temp $type");
|
||||
}
|
||||
push(@statements, "UPDATE $table SET ${column}_temp = $column");
|
||||
push(@statements, "COMMIT");
|
||||
push(@statements, "ALTER TABLE $table DROP COLUMN $column");
|
||||
push(@statements,
|
||||
"ALTER TABLE $table RENAME COLUMN ${column}_temp TO $column");
|
||||
} else {
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column $type");
|
||||
}
|
||||
|
||||
if ($new_def->{TYPE} =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
|
||||
push(@statements, _get_create_seq_ddl($table, $column));
|
||||
}
|
||||
|
||||
# If this column is no longer SERIAL, we need to drop the sequence
|
||||
# that went along with it.
|
||||
if ($old_def->{TYPE} =~ /serial/i && $new_def->{TYPE} !~ /serial/i) {
|
||||
push(@statements, "DROP SEQUENCE ${table}_${column}_SEQ");
|
||||
push(@statements, "DROP TRIGGER ${table}_${column}_TR");
|
||||
}
|
||||
|
||||
# If this column is changed to type TEXT/VARCHAR, we need to deal with
|
||||
# empty string.
|
||||
if ( $old_def->{TYPE} !~ /varchar|text/i
|
||||
&& $new_def->{TYPE} =~ /varchar|text/i
|
||||
&& $new_def->{NOTNULL} )
|
||||
{
|
||||
push (@statements, _get_notnull_trigger_ddl($table, $column));
|
||||
}
|
||||
# If this column is no longer TEXT/VARCHAR, we need to drop the trigger
|
||||
# that went along with it.
|
||||
if ( $old_def->{TYPE} =~ /varchar|text/i
|
||||
&& $old_def->{NOTNULL}
|
||||
&& $new_def->{TYPE} !~ /varchar|text/i )
|
||||
{
|
||||
push(@statements, "DROP TRIGGER ${table}_${column}");
|
||||
}
|
||||
return @statements;
|
||||
}
|
||||
|
||||
sub get_rename_column_ddl {
|
||||
my ($self, $table, $old_name, $new_name) = @_;
|
||||
if (lc($old_name) eq lc($new_name)) {
|
||||
# if the only change is a case change, return an empty list.
|
||||
return ();
|
||||
}
|
||||
my @sql = ("ALTER TABLE $table RENAME COLUMN $old_name TO $new_name");
|
||||
my $def = $self->get_column_abstract($table, $old_name);
|
||||
if ($def->{TYPE} =~ /SERIAL/i) {
|
||||
# We have to rename the series also, and fix the default of the series.
|
||||
push(@sql, "RENAME ${table}_${old_name}_SEQ TO
|
||||
${table}_${new_name}_seq");
|
||||
my $serial_sql =
|
||||
"CREATE OR REPLACE TRIGGER ${table}_${new_name}_TR "
|
||||
. " BEFORE INSERT ON ${table} "
|
||||
. " FOR EACH ROW "
|
||||
. " BEGIN "
|
||||
. " SELECT ${table}_${new_name}_SEQ.NEXTVAL "
|
||||
. " INTO :NEW.${new_name} FROM DUAL; "
|
||||
. " END;";
|
||||
push(@sql, $serial_sql);
|
||||
push(@sql, "DROP TRIGGER ${table}_${old_name}_TR");
|
||||
}
|
||||
if ($def->{TYPE} =~ /varchar|text/i && $def->{NOTNULL} ) {
|
||||
push(@sql, _get_notnull_trigger_ddl($table,$new_name));
|
||||
push(@sql, "DROP TRIGGER ${table}_${old_name}");
|
||||
}
|
||||
return @sql;
|
||||
}
|
||||
|
||||
sub _get_notnull_trigger_ddl {
|
||||
my ($table, $column) = @_;
|
||||
|
||||
my $notnull_sql = "CREATE OR REPLACE TRIGGER "
|
||||
. " ${table}_${column}"
|
||||
. " BEFORE INSERT OR UPDATE ON ". $table
|
||||
. " FOR EACH ROW"
|
||||
. " BEGIN "
|
||||
. " IF :NEW.". $column ." IS NULL THEN "
|
||||
. " SELECT '" . Bugzilla::DB::Oracle->EMPTY_STRING
|
||||
. "' INTO :NEW.". $column ." FROM DUAL; "
|
||||
. " END IF; "
|
||||
. " END ".$table.";";
|
||||
return $notnull_sql;
|
||||
}
|
||||
|
||||
sub _get_create_seq_ddl {
|
||||
my ($self, $table, $column, $start_with) = @_;
|
||||
$start_with ||= 1;
|
||||
my @ddl;
|
||||
my $seq_name = "${table}_${column}_SEQ";
|
||||
my $seq_sql = "CREATE SEQUENCE $seq_name "
|
||||
. " INCREMENT BY 1 "
|
||||
. " START WITH $start_with "
|
||||
. " NOMAXVALUE "
|
||||
. " NOCYCLE "
|
||||
. " NOCACHE";
|
||||
my $serial_sql = "CREATE OR REPLACE TRIGGER ${table}_${column}_TR "
|
||||
. " BEFORE INSERT ON ${table} "
|
||||
. " FOR EACH ROW "
|
||||
. " BEGIN "
|
||||
. " SELECT ${seq_name}.NEXTVAL "
|
||||
. " INTO :NEW.${column} FROM DUAL; "
|
||||
. " END;";
|
||||
push (@ddl, $seq_sql);
|
||||
push (@ddl, $serial_sql);
|
||||
|
||||
return @ddl;
|
||||
}
|
||||
|
||||
sub get_set_serial_sql {
|
||||
my ($self, $table, $column, $value) = @_;
|
||||
my @sql;
|
||||
my $seq_name = "${table}_${column}_SEQ";
|
||||
push(@sql, "DROP SEQUENCE ${seq_name}");
|
||||
push(@sql, $self->_get_create_seq_ddl($table, $column, $value));
|
||||
return @sql;
|
||||
}
|
||||
|
||||
sub get_drop_column_ddl {
|
||||
my $self = shift;
|
||||
my ($table, $column) = @_;
|
||||
my @sql;
|
||||
push(@sql, $self->SUPER::get_drop_column_ddl(@_));
|
||||
my $dbh=Bugzilla->dbh;
|
||||
my $trigger_name = uc($table . "_" . $column);
|
||||
my $exist_trigger = $dbh->selectcol_arrayref(
|
||||
"SELECT OBJECT_NAME FROM USER_OBJECTS
|
||||
WHERE OBJECT_NAME = ?", undef, $trigger_name);
|
||||
if(@$exist_trigger) {
|
||||
push(@sql, "DROP TRIGGER $trigger_name");
|
||||
}
|
||||
return @sql;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,173 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Andrew Dunstan <andrew@dunslane.net>,
|
||||
# Edward J. Sabol <edwardjsabol@iname.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::DB::Schema::Pg;
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# DB::Schema implementation for PostgreSQL
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
use strict;
|
||||
use base qw(Bugzilla::DB::Schema);
|
||||
use Storable qw(dclone);
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
sub _initialize {
|
||||
|
||||
my $self = shift;
|
||||
|
||||
$self = $self->SUPER::_initialize(@_);
|
||||
|
||||
# Remove FULLTEXT index types from the schemas.
|
||||
foreach my $table (keys %{ $self->{schema} }) {
|
||||
if ($self->{schema}{$table}{INDEXES}) {
|
||||
foreach my $index (@{ $self->{schema}{$table}{INDEXES} }) {
|
||||
if (ref($index) eq 'HASH') {
|
||||
delete($index->{TYPE}) if (exists $index->{TYPE}
|
||||
&& $index->{TYPE} eq 'FULLTEXT');
|
||||
}
|
||||
}
|
||||
foreach my $index (@{ $self->{abstract_schema}{$table}{INDEXES} }) {
|
||||
if (ref($index) eq 'HASH') {
|
||||
delete($index->{TYPE}) if (exists $index->{TYPE}
|
||||
&& $index->{TYPE} eq 'FULLTEXT');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$self->{db_specific} = {
|
||||
|
||||
BOOLEAN => 'smallint',
|
||||
FALSE => '0',
|
||||
TRUE => '1',
|
||||
|
||||
INT1 => 'integer',
|
||||
INT2 => 'integer',
|
||||
INT3 => 'integer',
|
||||
INT4 => 'integer',
|
||||
|
||||
SMALLSERIAL => 'serial unique',
|
||||
MEDIUMSERIAL => 'serial unique',
|
||||
INTSERIAL => 'serial unique',
|
||||
|
||||
TINYTEXT => 'varchar(255)',
|
||||
MEDIUMTEXT => 'text',
|
||||
LONGTEXT => 'text',
|
||||
|
||||
LONGBLOB => 'bytea',
|
||||
|
||||
DATETIME => 'timestamp(0) without time zone',
|
||||
|
||||
};
|
||||
|
||||
$self->_adjust_schema;
|
||||
|
||||
return $self;
|
||||
|
||||
} #eosub--_initialize
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
sub get_rename_column_ddl {
|
||||
my ($self, $table, $old_name, $new_name) = @_;
|
||||
if (lc($old_name) eq lc($new_name)) {
|
||||
# if the only change is a case change, return an empty list, since Pg
|
||||
# is case-insensitive and will return an error about a duplicate name
|
||||
return ();
|
||||
}
|
||||
my @sql = ("ALTER TABLE $table RENAME COLUMN $old_name TO $new_name");
|
||||
my $def = $self->get_column_abstract($table, $old_name);
|
||||
if ($def->{TYPE} =~ /SERIAL/i) {
|
||||
# We have to rename the series also, and fix the default of the series.
|
||||
push(@sql, "ALTER TABLE ${table}_${old_name}_seq
|
||||
RENAME TO ${table}_${new_name}_seq");
|
||||
push(@sql, "ALTER TABLE $table ALTER COLUMN $new_name
|
||||
SET DEFAULT NEXTVAL('${table}_${new_name}_seq')");
|
||||
}
|
||||
return @sql;
|
||||
}
|
||||
|
||||
sub get_rename_table_sql {
|
||||
my ($self, $old_name, $new_name) = @_;
|
||||
if (lc($old_name) eq lc($new_name)) {
|
||||
# if the only change is a case change, return an empty list, since Pg
|
||||
# is case-insensitive and will return an error about a duplicate name
|
||||
return ();
|
||||
}
|
||||
return ("ALTER TABLE $old_name RENAME TO $new_name");
|
||||
}
|
||||
|
||||
sub get_set_serial_sql {
|
||||
my ($self, $table, $column, $value) = @_;
|
||||
return ("SELECT setval('${table}_${column}_seq', $value, false)
|
||||
FROM $table");
|
||||
}
|
||||
|
||||
sub _get_alter_type_sql {
|
||||
my ($self, $table, $column, $new_def, $old_def) = @_;
|
||||
my @statements;
|
||||
|
||||
my $type = $new_def->{TYPE};
|
||||
$type = $self->{db_specific}->{$type}
|
||||
if exists $self->{db_specific}->{$type};
|
||||
|
||||
if ($type =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
|
||||
die("You cannot specify a DEFAULT on a SERIAL-type column.")
|
||||
if $new_def->{DEFAULT};
|
||||
}
|
||||
|
||||
$type =~ s/\bserial\b/integer/i;
|
||||
|
||||
# On Pg, you don't need UNIQUE if you're a PK--it creates
|
||||
# two identical indexes otherwise.
|
||||
$type =~ s/unique//i if $new_def->{PRIMARYKEY};
|
||||
|
||||
push(@statements, "ALTER TABLE $table ALTER COLUMN $column
|
||||
TYPE $type");
|
||||
|
||||
if ($new_def->{TYPE} =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
|
||||
push(@statements, "CREATE SEQUENCE ${table}_${column}_seq");
|
||||
push(@statements, "SELECT setval('${table}_${column}_seq',
|
||||
MAX($table.$column))
|
||||
FROM $table");
|
||||
push(@statements, "ALTER TABLE $table ALTER COLUMN $column
|
||||
SET DEFAULT nextval('${table}_${column}_seq')");
|
||||
}
|
||||
|
||||
# If this column is no longer SERIAL, we need to drop the sequence
|
||||
# that went along with it.
|
||||
if ($old_def->{TYPE} =~ /serial/i && $new_def->{TYPE} !~ /serial/i) {
|
||||
push(@statements, "ALTER TABLE $table ALTER COLUMN $column
|
||||
DROP DEFAULT");
|
||||
# XXX Pg actually won't let us drop the sequence, even though it's
|
||||
# no longer in use. So we harmlessly leave behind a sequence
|
||||
# that does nothing.
|
||||
#push(@statements, "DROP SEQUENCE ${table}_${column}_seq");
|
||||
}
|
||||
|
||||
return @statements;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,270 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Bradley Baetz <bbaetz@acm.org>
|
||||
# Marc Schumann <wurblzap@gmail.com>
|
||||
# Frédéric Buclin <LpSolit@gmail.com>
|
||||
|
||||
package Bugzilla::Error;
|
||||
|
||||
use strict;
|
||||
use base qw(Exporter);
|
||||
|
||||
@Bugzilla::Error::EXPORT = qw(ThrowCodeError ThrowTemplateError ThrowUserError);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::Util;
|
||||
|
||||
use Carp;
|
||||
use Data::Dumper;
|
||||
use Date::Format;
|
||||
|
||||
# We cannot use $^S to detect if we are in an eval(), because mod_perl
|
||||
# already eval'uates everything, so $^S = 1 in all cases under mod_perl!
|
||||
sub _in_eval {
|
||||
my $in_eval = 0;
|
||||
for (my $stack = 1; my $sub = (caller($stack))[3]; $stack++) {
|
||||
last if $sub =~ /^ModPerl/;
|
||||
$in_eval = 1 if $sub =~ /^\(eval\)/;
|
||||
}
|
||||
return $in_eval;
|
||||
}
|
||||
|
||||
sub _throw_error {
|
||||
my ($name, $error, $vars) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$vars ||= {};
|
||||
|
||||
$vars->{error} = $error;
|
||||
# Don't show function arguments, in case they contain confidential data.
|
||||
local $Carp::MaxArgNums = -1;
|
||||
# Don't show the error as coming from Bugzilla::Error, show it as coming
|
||||
# from the caller.
|
||||
local $Carp::CarpInternal{'Bugzilla::Error'} = 1;
|
||||
$vars->{traceback} = Carp::longmess();
|
||||
|
||||
# Make sure any transaction is rolled back (if supported).
|
||||
# If we are within an eval(), do not roll back transactions as we are
|
||||
# eval'uating some test on purpose.
|
||||
$dbh->bz_rollback_transaction() if ($dbh->bz_in_transaction() && !_in_eval());
|
||||
|
||||
my $datadir = bz_locations()->{'datadir'};
|
||||
# If a writable $datadir/errorlog exists, log error details there.
|
||||
if (-w "$datadir/errorlog") {
|
||||
require Data::Dumper;
|
||||
my $mesg = "";
|
||||
for (1..75) { $mesg .= "-"; };
|
||||
$mesg .= "\n[$$] " . time2str("%D %H:%M:%S ", time());
|
||||
$mesg .= "$name $error ";
|
||||
$mesg .= remote_ip();
|
||||
$mesg .= Bugzilla->user->login;
|
||||
$mesg .= (' actually ' . Bugzilla->sudoer->login) if Bugzilla->sudoer;
|
||||
$mesg .= "\n";
|
||||
my %params = Bugzilla->cgi->Vars;
|
||||
$Data::Dumper::Useqq = 1;
|
||||
for my $param (sort keys %params) {
|
||||
my $val = $params{$param};
|
||||
# obscure passwords
|
||||
$val = "*****" if $param =~ /password/i;
|
||||
# limit line length
|
||||
$val =~ s/^(.{512}).*$/$1\[CHOP\]/;
|
||||
$mesg .= "[$$] " . Data::Dumper->Dump([$val],["param($param)"]);
|
||||
}
|
||||
for my $var (sort keys %ENV) {
|
||||
my $val = $ENV{$var};
|
||||
$val = "*****" if $val =~ /password|http_pass/i;
|
||||
$mesg .= "[$$] " . Data::Dumper->Dump([$val],["env($var)"]);
|
||||
}
|
||||
open(ERRORLOGFID, ">>", "$datadir/errorlog");
|
||||
print ERRORLOGFID "$mesg\n";
|
||||
close ERRORLOGFID;
|
||||
}
|
||||
|
||||
my $template = Bugzilla->template;
|
||||
if (Bugzilla->error_mode == ERROR_MODE_WEBPAGE) {
|
||||
print Bugzilla->cgi->header();
|
||||
$template->process($name, $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
# There are some tests that throw and catch a lot of errors,
|
||||
# and calling $template->process over and over for those errors
|
||||
# is too slow. So instead, we just "die" with a dump of the arguments.
|
||||
elsif (Bugzilla->error_mode == ERROR_MODE_TEST) {
|
||||
die Dumper($vars);
|
||||
}
|
||||
else {
|
||||
my $message;
|
||||
$template->process($name, $vars, \$message)
|
||||
|| ThrowTemplateError($template->error());
|
||||
if (Bugzilla->error_mode == ERROR_MODE_DIE) {
|
||||
die("$message\n");
|
||||
}
|
||||
elsif (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT
|
||||
|| Bugzilla->error_mode == ERROR_MODE_JSON_RPC)
|
||||
{
|
||||
# Clone the hash so we aren't modifying the constant.
|
||||
my %error_map = %{ WS_ERROR_CODE() };
|
||||
require Bugzilla::Hook;
|
||||
Bugzilla::Hook::process('webservice_error_codes',
|
||||
{ error_map => \%error_map });
|
||||
my $code = $error_map{$error};
|
||||
if (!$code) {
|
||||
$code = ERROR_UNKNOWN_FATAL if $name =~ /code/i;
|
||||
$code = ERROR_UNKNOWN_TRANSIENT if $name =~ /user/i;
|
||||
}
|
||||
|
||||
if (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT) {
|
||||
die SOAP::Fault->faultcode($code)->faultstring($message);
|
||||
}
|
||||
else {
|
||||
my $server = Bugzilla->_json_server;
|
||||
# Technically JSON-RPC isn't allowed to have error numbers
|
||||
# higher than 999, but we do this to avoid conflicts with
|
||||
# the internal JSON::RPC error codes.
|
||||
$server->raise_error(code => 100000 + $code,
|
||||
message => $message,
|
||||
id => $server->{_bz_request_id},
|
||||
version => $server->version);
|
||||
# Most JSON-RPC Throw*Error calls happen within an eval inside
|
||||
# of JSON::RPC. So, in that circumstance, instead of exiting,
|
||||
# we die with no message. JSON::RPC checks raise_error before
|
||||
# it checks $@, so it returns the proper error.
|
||||
die if _in_eval();
|
||||
$server->response($server->error_response_header);
|
||||
}
|
||||
}
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
sub ThrowUserError {
|
||||
_throw_error("global/user-error.html.tmpl", @_);
|
||||
}
|
||||
|
||||
sub ThrowCodeError {
|
||||
_throw_error("global/code-error.html.tmpl", @_);
|
||||
}
|
||||
|
||||
sub ThrowTemplateError {
|
||||
my ($template_err) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# Make sure the transaction is rolled back (if supported).
|
||||
$dbh->bz_rollback_transaction() if $dbh->bz_in_transaction();
|
||||
|
||||
my $vars = {};
|
||||
if (Bugzilla->error_mode == ERROR_MODE_DIE) {
|
||||
die("error: template error: $template_err");
|
||||
}
|
||||
|
||||
$vars->{'template_error_msg'} = $template_err;
|
||||
$vars->{'error'} = "template_error";
|
||||
|
||||
my $template = Bugzilla->template;
|
||||
|
||||
# Try a template first; but if this one fails too, fall back
|
||||
# on plain old print statements.
|
||||
if (!$template->process("global/code-error.html.tmpl", $vars)) {
|
||||
my $maintainer = Bugzilla->params->{'maintainer'};
|
||||
my $error = html_quote($vars->{'template_error_msg'});
|
||||
my $error2 = html_quote($template->error());
|
||||
print <<END;
|
||||
<tt>
|
||||
<p>
|
||||
Bugzilla has suffered an internal error. Please save this page and
|
||||
send it to $maintainer with details of what you were doing at the
|
||||
time this message appeared.
|
||||
</p>
|
||||
<script type="text/javascript"> <!--
|
||||
document.write("<p>URL: " +
|
||||
document.location.href.replace(/&/g,"&")
|
||||
.replace(/</g,"<")
|
||||
.replace(/>/g,">") + "</p>");
|
||||
// -->
|
||||
</script>
|
||||
<p>Template->process() failed twice.<br>
|
||||
First error: $error<br>
|
||||
Second error: $error2</p>
|
||||
</tt>
|
||||
END
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Error - Error handling utilities for Bugzilla
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Error;
|
||||
|
||||
ThrowUserError("error_tag",
|
||||
{ foo => 'bar' });
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Various places throughout the Bugzilla codebase need to report errors to the
|
||||
user. The C<Throw*Error> family of functions allow this to be done in a
|
||||
generic and localizable manner.
|
||||
|
||||
These functions automatically unlock the database tables, if there were any
|
||||
locked. They will also roll back the transaction, if it is supported by
|
||||
the underlying DB.
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<ThrowUserError>
|
||||
|
||||
This function takes an error tag as the first argument, and an optional hashref
|
||||
of variables as a second argument. These are used by the
|
||||
I<global/user-error.html.tmpl> template to format the error, using the passed
|
||||
in variables as required.
|
||||
|
||||
=item C<ThrowCodeError>
|
||||
|
||||
This function is used when an internal check detects an error of some sort.
|
||||
This usually indicates a bug in Bugzilla, although it can occur if the user
|
||||
manually constructs urls without correct parameters.
|
||||
|
||||
This function's behaviour is similar to C<ThrowUserError>, except that the
|
||||
template used to display errors is I<global/code-error.html.tmpl>. In addition
|
||||
if the hashref used as the optional second argument contains a key I<variables>
|
||||
then the contents of the hashref (which is expected to be another hashref) will
|
||||
be displayed after the error message, as a debugging aid.
|
||||
|
||||
=item C<ThrowTemplateError>
|
||||
|
||||
This function should only be called if a C<template-<gt>process()> fails.
|
||||
It tries another template first, because often one template being
|
||||
broken or missing doesn't mean that they all are. But it falls back to
|
||||
a print statement as a last-ditch error.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla|Bugzilla>
|
||||
@@ -1,813 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Everything Solved, Inc.
|
||||
# Portions created by the Initial Developers are Copyright (C) 2009 the
|
||||
# Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Extension;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Install::Util qw(
|
||||
extension_code_files extension_template_directory
|
||||
extension_package_directory);
|
||||
|
||||
use File::Basename;
|
||||
use File::Spec;
|
||||
|
||||
####################
|
||||
# Subclass Methods #
|
||||
####################
|
||||
|
||||
sub new {
|
||||
my ($class, $params) = @_;
|
||||
$params ||= {};
|
||||
bless $params, $class;
|
||||
return $params;
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Class (Bugzilla::Extension) Methods #
|
||||
#######################################
|
||||
|
||||
sub load {
|
||||
my ($class, $extension_file, $config_file) = @_;
|
||||
my $package;
|
||||
|
||||
# This is needed during checksetup.pl, because Extension packages can
|
||||
# only be loaded once (they return "1" the second time they're loaded,
|
||||
# instead of their name). During checksetup.pl, extensions are loaded
|
||||
# once by Bugzilla::Install::Requirements, and then later again via
|
||||
# Bugzilla->extensions (because of hooks).
|
||||
my $map = Bugzilla->request_cache->{extension_requirement_package_map};
|
||||
|
||||
if ($config_file) {
|
||||
if ($map and defined $map->{$config_file}) {
|
||||
$package = $map->{$config_file};
|
||||
}
|
||||
else {
|
||||
my $name = require $config_file;
|
||||
if ($name =~ /^\d+$/) {
|
||||
ThrowCodeError('extension_must_return_name',
|
||||
{ extension => $config_file,
|
||||
returned => $name });
|
||||
}
|
||||
$package = "${class}::$name";
|
||||
}
|
||||
|
||||
__do_call($package, 'modify_inc', $config_file);
|
||||
}
|
||||
|
||||
if ($map and defined $map->{$extension_file}) {
|
||||
$package = $map->{$extension_file};
|
||||
$package->modify_inc($extension_file) if !$config_file;
|
||||
}
|
||||
else {
|
||||
my $name = require $extension_file;
|
||||
if ($name =~ /^\d+$/) {
|
||||
ThrowCodeError('extension_must_return_name',
|
||||
{ extension => $extension_file, returned => $name });
|
||||
}
|
||||
$package = "${class}::$name";
|
||||
$package->modify_inc($extension_file) if !$config_file;
|
||||
}
|
||||
|
||||
$class->_validate_package($package, $extension_file);
|
||||
return $package;
|
||||
}
|
||||
|
||||
sub _validate_package {
|
||||
my ($class, $package, $extension_file) = @_;
|
||||
|
||||
# For extensions from data/extensions/additional, we don't have a file
|
||||
# name, so we fake it.
|
||||
if (!$extension_file) {
|
||||
$extension_file = $package;
|
||||
$extension_file =~ s/::/\//g;
|
||||
$extension_file .= '.pm';
|
||||
}
|
||||
|
||||
if (!eval { $package->NAME }) {
|
||||
ThrowCodeError('extension_no_name',
|
||||
{ filename => $extension_file, package => $package });
|
||||
}
|
||||
|
||||
if (!$package->isa($class)) {
|
||||
ThrowCodeError('extension_must_be_subclass',
|
||||
{ filename => $extension_file,
|
||||
package => $package,
|
||||
class => $class });
|
||||
}
|
||||
}
|
||||
|
||||
sub load_all {
|
||||
my $class = shift;
|
||||
my ($file_sets, $extra_packages) = extension_code_files();
|
||||
my @packages;
|
||||
foreach my $file_set (@$file_sets) {
|
||||
my $package = $class->load(@$file_set);
|
||||
push(@packages, $package);
|
||||
}
|
||||
|
||||
# Extensions from data/extensions/additional
|
||||
foreach my $package (@$extra_packages) {
|
||||
# Don't load an "additional" extension if we already have an extension
|
||||
# loaded with that name.
|
||||
next if grep($_ eq $package, @packages);
|
||||
# Untaint the package name
|
||||
$package =~ /([\w:]+)/;
|
||||
$package = $1;
|
||||
eval("require $package") || die $@;
|
||||
$package->_validate_package($package);
|
||||
push(@packages, $package);
|
||||
}
|
||||
|
||||
return \@packages;
|
||||
}
|
||||
|
||||
# Modifies @INC so that extensions can use modules like
|
||||
# "use Bugzilla::Extension::Foo::Bar", when Bar.pm is in the lib/
|
||||
# directory of the extension.
|
||||
sub modify_inc {
|
||||
my ($class, $file) = @_;
|
||||
|
||||
# Note that this package_dir call is necessary to set things up
|
||||
# for my_inc, even if we didn't take its return value.
|
||||
my $package_dir = __do_call($class, 'package_dir', $file);
|
||||
# Don't modify @INC for extensions that are just files in the extensions/
|
||||
# directory. We don't want Bugzilla's base lib/CGI.pm being loaded as
|
||||
# Bugzilla::Extension::Foo::CGI or any other confusing thing like that.
|
||||
return if $package_dir eq bz_locations->{'extensionsdir'};
|
||||
unshift(@INC, sub { __do_call($class, 'my_inc', @_) });
|
||||
}
|
||||
|
||||
# This is what gets put into @INC by modify_inc.
|
||||
sub my_inc {
|
||||
my ($class, undef, $file) = @_;
|
||||
|
||||
# This avoids infinite recursion in case anything inside of this function
|
||||
# does a "require". (I know for sure that File::Spec->case_tolerant does
|
||||
# a "require" on Windows, for example.)
|
||||
return if $file !~ /^Bugzilla/;
|
||||
|
||||
my $lib_dir = __do_call($class, 'lib_dir');
|
||||
my @class_parts = split('::', $class);
|
||||
my ($vol, $dir, $file_name) = File::Spec->splitpath($file);
|
||||
my @dir_parts = File::Spec->splitdir($dir);
|
||||
# File::Spec::Win32 (any maybe other OSes) add an empty directory at the
|
||||
# end of @dir_parts.
|
||||
@dir_parts = grep { $_ ne '' } @dir_parts;
|
||||
# Validate that this is a sub-package of Bugzilla::Extension::Foo ($class).
|
||||
for (my $i = 0; $i < scalar(@class_parts); $i++) {
|
||||
return if !@dir_parts;
|
||||
if (File::Spec->case_tolerant) {
|
||||
return if lc($class_parts[$i]) ne lc($dir_parts[0]);
|
||||
}
|
||||
else {
|
||||
return if $class_parts[$i] ne $dir_parts[0];
|
||||
}
|
||||
shift(@dir_parts);
|
||||
}
|
||||
# For Bugzilla::Extension::Foo::Bar, this would look something like
|
||||
# extensions/Example/lib/Bar.pm
|
||||
my $resolved_path = File::Spec->catfile($lib_dir, @dir_parts, $file_name);
|
||||
open(my $fh, '<', $resolved_path);
|
||||
return $fh;
|
||||
}
|
||||
|
||||
####################
|
||||
# Instance Methods #
|
||||
####################
|
||||
|
||||
use constant enabled => 1;
|
||||
|
||||
sub lib_dir {
|
||||
my $invocant = shift;
|
||||
my $package_dir = __do_call($invocant, 'package_dir');
|
||||
# For extensions that are just files in the extensions/ directory,
|
||||
# use the base lib/ dir as our "lib_dir". Note that Bugzilla never
|
||||
# uses lib_dir in this case, though, because modify_inc is prevented
|
||||
# from modifying @INC when we're just a file in the extensions/ directory.
|
||||
# So this particular code block exists just to make lib_dir return
|
||||
# something right in case an extension needs it for some odd reason.
|
||||
if ($package_dir eq bz_locations()->{'extensionsdir'}) {
|
||||
return bz_locations->{'ext_libpath'};
|
||||
}
|
||||
return File::Spec->catdir($package_dir, 'lib');
|
||||
}
|
||||
|
||||
sub template_dir { return extension_template_directory(@_); }
|
||||
sub package_dir { return extension_package_directory(@_); }
|
||||
|
||||
######################
|
||||
# Helper Subroutines #
|
||||
######################
|
||||
|
||||
# In order to not conflict with extensions' private subroutines, any helpers
|
||||
# here should start with a double underscore.
|
||||
|
||||
# This is for methods that can optionally be overridden in Config.pm.
|
||||
# It falls back to the local implementation if $class cannot do
|
||||
# the method. This is necessary because Config.pm is not a subclass of
|
||||
# Bugzilla::Extension.
|
||||
sub __do_call {
|
||||
my ($class, $method, @args) = @_;
|
||||
if ($class->can($method)) {
|
||||
return $class->$method(@args);
|
||||
}
|
||||
my $function_ref;
|
||||
{ no strict 'refs'; $function_ref = \&{$method}; }
|
||||
return $function_ref->($class, @args);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Extension - Base class for Bugzilla Extensions.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
The following would be in F<extensions/Foo/Extension.pm> or
|
||||
F<extensions/Foo.pm>:
|
||||
|
||||
package Bugzilla::Extension::Foo
|
||||
use strict;
|
||||
use base qw(Bugzilla::Extension);
|
||||
|
||||
our $VERSION = '0.02';
|
||||
use constant NAME => 'Foo';
|
||||
|
||||
sub some_hook_name { ... }
|
||||
|
||||
__PACKAGE__->NAME;
|
||||
|
||||
Custom templates would go into F<extensions/Foo/template/en/default/>.
|
||||
L<Template hooks|/Template Hooks> would go into
|
||||
F<extensions/Foo/template/en/default/hook/>.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is the base class for all Bugzilla extensions.
|
||||
|
||||
=head1 WRITING EXTENSIONS
|
||||
|
||||
The L</SYNOPSIS> above gives a pretty good overview of what's basically
|
||||
required to write an extension. This section gives more information
|
||||
on exactly how extensions work and how you write them. There is also a
|
||||
L<wiki page|https://wiki.mozilla.org/Bugzilla:Extension_Notes> with additional HOWTOs, tips and tricks.
|
||||
|
||||
=head2 Using F<extensions/create.pl>
|
||||
|
||||
There is a script, L<extensions::create>, that will set up the framework
|
||||
of a new extension for you. To use it, pick a name for your extension
|
||||
and, in the base bugzilla directory, do:
|
||||
|
||||
C<extensions/create.pl NAME>
|
||||
|
||||
But replace C<NAME> with the name you picked for your extension. That
|
||||
will create a new directory in the F<extensions/> directory with the name
|
||||
of your extension. The directory will contain a full framework for
|
||||
a new extension, with helpful comments in each file describing things
|
||||
about them.
|
||||
|
||||
=head2 Example Extension
|
||||
|
||||
There is a sample extension in F<extensions/Example/> that demonstrates
|
||||
most of the things described in this document, so if you find the
|
||||
documentation confusing, try just reading the code instead.
|
||||
|
||||
=head2 Where Extension Code Goes
|
||||
|
||||
Extension code lives under the F<extensions/> directory in Bugzilla.
|
||||
|
||||
There are two ways to write extensions:
|
||||
|
||||
=over
|
||||
|
||||
=item 1
|
||||
|
||||
If your extension will have only code and no templates or other files,
|
||||
you can create a simple C<.pm> file in the F<extensions/> directory.
|
||||
|
||||
For example, if you wanted to create an extension called "Foo" using this
|
||||
method, you would put your code into a file called F<extensions/Foo.pm>.
|
||||
|
||||
=item 2
|
||||
|
||||
If you plan for your extension to have templates and other files, you
|
||||
can create a whole directory for your extension, and the main extension
|
||||
code would go into a file called F<Extension.pm> in that directory.
|
||||
|
||||
For example, if you wanted to create an extension called "Foo" using this
|
||||
method, you would put your code into a file called
|
||||
F<extensions/Foo/Extension.pm>.
|
||||
|
||||
=back
|
||||
|
||||
=head2 The Extension C<NAME>.
|
||||
|
||||
The "name" of an extension shows up in several places:
|
||||
|
||||
=over
|
||||
|
||||
=item 1
|
||||
|
||||
The name of the package:
|
||||
|
||||
C<package Bugzilla::Extension::Foo;>
|
||||
|
||||
=item 2
|
||||
|
||||
In a C<NAME> constant that B<must> be defined for every extension:
|
||||
|
||||
C<< use constant NAME => 'Foo'; >>
|
||||
|
||||
=item 3
|
||||
|
||||
At the very end of the file:
|
||||
|
||||
C<< __PACKAGE__->NAME; >>
|
||||
|
||||
You'll notice that though most Perl packages end with C<1;>, Bugzilla
|
||||
Extensions must B<always> end with C<< __PACKAGE__->NAME; >>.
|
||||
|
||||
=back
|
||||
|
||||
The name must be identical in all of those locations.
|
||||
|
||||
=head2 Hooks
|
||||
|
||||
In L<Bugzilla::Hook>, there is a L<list of hooks|Bugzilla::Hook/HOOKS>.
|
||||
These are the various areas of Bugzilla that an extension can "hook" into,
|
||||
which allow your extension to perform code during that point in Bugzilla's
|
||||
execution.
|
||||
|
||||
If your extension wants to implement a hook, all you have to do is
|
||||
write a subroutine in your hook package that has the same name as
|
||||
the hook. The subroutine will be called as a method on your extension,
|
||||
and it will get the arguments specified in the hook's documentation as
|
||||
named parameters in a hashref.
|
||||
|
||||
For example, here's an implementation of a hook named C<foo_start>
|
||||
that gets an argument named C<bar>:
|
||||
|
||||
sub foo_start {
|
||||
my ($self, $args) = @_;
|
||||
my $bar = $args->{bar};
|
||||
print "I got $bar!\n";
|
||||
}
|
||||
|
||||
And that would go into your extension's code file--the file that was
|
||||
described in the L</Where Extension Code Goes> section above.
|
||||
|
||||
During your subroutine, you may want to know what values were passed
|
||||
as CGI arguments to the current script, or what arguments were passed to
|
||||
the current WebService method. You can get that data via
|
||||
L<Bugzilla/input_params>.
|
||||
|
||||
=head3 Adding New Hooks To Bugzilla
|
||||
|
||||
If you need a new hook for your extension and you want that hook to be
|
||||
added to Bugzilla itself, see our development process at
|
||||
L<http://wiki.mozilla.org/Bugzilla:Developers>.
|
||||
|
||||
In order for a new hook to be accepted into Bugzilla, it has to work,
|
||||
it must have documentation in L<Bugzilla::Hook>, and it must have example
|
||||
code in F<extensions/Example/Extension.pm>.
|
||||
|
||||
One question that is often asked about new hooks is, "Is this the most
|
||||
flexible way to implement this hook?" That is, the more power extension
|
||||
authors get from a hook, the more likely it is to be accepted into Bugzilla.
|
||||
Hooks that only hook a very specific part of Bugzilla will not be accepted
|
||||
if their functionality can be accomplished equally well with a more generic
|
||||
hook.
|
||||
|
||||
=head2 If Your Extension Requires Certain Perl Modules
|
||||
|
||||
If there are certain Perl modules that your extension requires in order
|
||||
to run, there is a way you can tell Bugzilla this, and then L<checksetup>
|
||||
will make sure that those modules are installed, when you run L<checksetup>.
|
||||
|
||||
To do this, you need to specify a constant called C<REQUIRED_MODULES>
|
||||
in your extension. This constant has the same format as
|
||||
L<Bugzilla::Install::Requirements/REQUIRED_MODULES>.
|
||||
|
||||
If there are optional modules that add additional functionality to your
|
||||
application, you can specify them in a constant called OPTIONAL_MODULES,
|
||||
which has the same format as
|
||||
L<Bugzilla::Install::Requirements/OPTIONAL_MODULES>.
|
||||
|
||||
=head3 If Your Extension Needs Certain Modules In Order To Compile
|
||||
|
||||
If your extension needs a particular Perl module in order to
|
||||
I<compile>, then you have a "chicken and egg" problem--in order to
|
||||
read C<REQUIRED_MODULES>, we have to compile your extension. In order
|
||||
to compile your extension, we need to already have the modules in
|
||||
C<REQUIRED_MODULES>!
|
||||
|
||||
To get around this problem, Bugzilla allows you to have an additional
|
||||
file, besides F<Extension.pm>, called F<Config.pm>, that contains
|
||||
just C<REQUIRED_MODULES>. If you have a F<Config.pm>, it must also
|
||||
contain the C<NAME> constant, instead of your main F<Extension.pm>
|
||||
containing the C<NAME> constant.
|
||||
|
||||
The contents of the file would look something like this for an extension
|
||||
named C<Foo>:
|
||||
|
||||
package Bugzilla::Extension::Foo;
|
||||
use strict;
|
||||
use constant NAME => 'Foo';
|
||||
use constant REQUIRED_MODULES => [
|
||||
{
|
||||
package => 'Some-Package',
|
||||
module => 'Some::Module',
|
||||
version => 0,
|
||||
}
|
||||
];
|
||||
__PACKAGE__->NAME;
|
||||
|
||||
Note that it is I<not> a subclass of C<Bugzilla::Extension>, because
|
||||
at the time that module requirements are being checked in L<checksetup>,
|
||||
C<Bugzilla::Extension> cannot be loaded. Also, just like F<Extension.pm>,
|
||||
it ends with C<< __PACKAGE__->NAME; >>. Note also that it has the
|
||||
B<exact same> C<package> name as F<Extension.pm>.
|
||||
|
||||
This file may not use any Perl modules other than L<Bugzilla::Constants>,
|
||||
L<Bugzilla::Install::Util>, L<Bugzilla::Install::Requirements>, and
|
||||
modules that ship with Perl itself.
|
||||
|
||||
If you want to define both C<REQUIRED_MODULES> and C<OPTIONAL_MODULES>,
|
||||
they must both be in F<Config.pm> or both in F<Extension.pm>.
|
||||
|
||||
Every time your extension is loaded by Bugzilla, F<Config.pm> will be
|
||||
read and then F<Extension.pm> will be read, so your methods in F<Extension.pm>
|
||||
will have access to everything in F<Config.pm>. Don't define anything
|
||||
with an identical name in both files, or Perl may throw a warning that
|
||||
you are redefining things.
|
||||
|
||||
This method of setting C<REQUIRED_MODULES> is of course not available if
|
||||
your extension is a single file named C<Foo.pm>.
|
||||
|
||||
If any of this is confusing, just look at the code of the Example extension.
|
||||
It uses this method to specify requirements.
|
||||
|
||||
=head2 Libraries
|
||||
|
||||
Extensions often want to have their own Perl modules. Your extension
|
||||
can load any Perl module in its F<lib/> directory. (So, if your extension is
|
||||
F<extensions/Foo/>, then your Perl modules go into F<extensions/Foo/lib/>.)
|
||||
|
||||
However, the C<package> name of your libraries will not work quite
|
||||
like normal Perl modules do. F<extensions/Foo/lib/Bar.pm> is
|
||||
loaded as C<Bugzilla::Extension::Foo::Bar>. Or, to say it another way,
|
||||
C<use Bugzilla::Extension::Foo::Bar;> loads F<extensions/Foo/lib/Bar.pm>,
|
||||
which should have C<package Bugzilla::Extension::Foo::Bar;> as its package
|
||||
name.
|
||||
|
||||
This allows any place in Bugzilla to load your modules, which is important
|
||||
for some hooks. It even allows other extensions to load your modules, and
|
||||
allows you to install your modules into the global Perl install
|
||||
as F<Bugzilla/Extension/Foo/Bar.pm>, if you'd like, which helps allow CPAN
|
||||
distribution of Bugzilla extensions.
|
||||
|
||||
B<Note:> If you want to C<use> or C<require> a module that's in
|
||||
F<extensions/Foo/lib/> at the top level of your F<Extension.pm>,
|
||||
you must have a F<Config.pm> (see above) with at least the C<NAME>
|
||||
constant defined in it.
|
||||
|
||||
=head2 Templates
|
||||
|
||||
Extensions store templates in a C<template> subdirectory of the extension.
|
||||
(Obviously, this isn't available for extensions that aren't a directory.)
|
||||
|
||||
The format of this directory is exactly like the normal layout of Bugzilla's
|
||||
C<template> directory--in fact, your extension's C<template> directory
|
||||
becomes part of Bugzilla's template "search path" as described in
|
||||
L<Bugzilla::Install::Util/template_include_path>.
|
||||
|
||||
You can actually include templates in your extension without having any
|
||||
C<.pm> files in your extension at all, if you want. (That is, it's entirely
|
||||
valid to have an extension that's just template files and no code files.)
|
||||
|
||||
Bugzilla's templates are written in a language called Template Toolkit.
|
||||
You can find out more about Template Toolkit at L<http://template-toolkit.org>.
|
||||
|
||||
There are two ways to extend or modify Bugzilla's templates: you can use
|
||||
template hooks (described below) or you can override existing templates
|
||||
entirely (described further down).
|
||||
|
||||
=head2 Template Hooks
|
||||
|
||||
Templates can be extended using a system of "hooks" that add new UI elements
|
||||
to a particular area of Bugzilla without modifying the code of the existing
|
||||
templates. This is the recommended way for extensions to modify the user
|
||||
interface of Bugzilla.
|
||||
|
||||
=head3 Which Templates Can Be Hooked
|
||||
|
||||
There is no list of template hooks like there is for standard code hooks.
|
||||
To find what places in the user interface can be hooked, search for the
|
||||
string C<Hook.process> in Bugzilla's templates (in the
|
||||
F<template/en/default/> directory). That will also give you the name of
|
||||
the hooks--the first argument to C<Hook.process> is the name of the hook.
|
||||
(A later section in this document explains how to use that name).
|
||||
|
||||
For example, if you see C<Hook.process("additional_header")>, that means
|
||||
the name of the hook is C<additional_header>.
|
||||
|
||||
=head3 Where Template Hooks Go
|
||||
|
||||
To extend templates in your extension using template hooks, you put files into
|
||||
the F<template/en/default/hook> directory of your extension. So, if you had an
|
||||
extension called "Foo", your template extensions would go into
|
||||
F<extensions/Foo/template/en/default/hook/>.
|
||||
|
||||
(Note that the base F<template/en/default/hook> directory in Bugzilla itself
|
||||
also works, although you would never use that for an extension that you
|
||||
intended to distribute.)
|
||||
|
||||
The files that go into this directory have a certain name, based on the
|
||||
name of the template that is being hooked, and the name of the hook.
|
||||
For example, let's imagine that you have an extension named "Foo",
|
||||
and you want to use the C<additional_header> hook in
|
||||
F<template/en/default/global/header.html.tmpl>. Your code would go into
|
||||
F<extensions/Foo/template/en/default/hook/global/header-additional_header.html.tmpl>. Any code you put into that file will happen at the point that
|
||||
C<Hook.process("additional_header")> is called in
|
||||
F<template/en/default/global/header.html.tmpl>.
|
||||
|
||||
As you can see, template extension file names follow a pattern. The
|
||||
pattern looks like:
|
||||
|
||||
<templates>/hook/<template path>/<template name>-<hook name>.<template type>.tmpl
|
||||
|
||||
=over
|
||||
|
||||
=item <templates>
|
||||
|
||||
This is the full path to the template directory, like
|
||||
F<extensions/Foo/template/en/default>. This works much like normal templates
|
||||
do, in the sense that template extensions in C<custom> override template
|
||||
extensions in C<default> for your extension, templates for different languages
|
||||
can be supplied, etc. Template extensions are searched for and run in the
|
||||
order described in L<Bugzilla::Install::Util/template_include_path>.
|
||||
|
||||
The difference between normal templates and template hooks is that hooks
|
||||
will be run for I<every> extension, whereas for normal templates, Bugzilla
|
||||
just takes the first one it finds and stops searching. So while a template
|
||||
extension in the C<custom> directory may override the same-named template
|
||||
extension in the C<default> directory I<within your Bugzilla extension>,
|
||||
it will not override the same-named template extension in the C<default>
|
||||
directory of another Bugzilla extension.
|
||||
|
||||
=item <template path>
|
||||
|
||||
This is the part of the path (excluding the filename) that comes after
|
||||
F<template/en/default/> in a template's path. So, for
|
||||
F<template/en/default/global/header.html.tmpl>, this would simply be
|
||||
C<global>.
|
||||
|
||||
=item <template name>
|
||||
|
||||
This is the file name of the template, before the C<.html.tmpl> part.
|
||||
So, for F<template/en/default/global/header.html.tmpl>, this would be
|
||||
C<header>.
|
||||
|
||||
=item <hook name>
|
||||
|
||||
This is the name of the hook--what you saw in C<Hook.process> inside
|
||||
of the template you want to hook. In our example, this is
|
||||
C<additional_header>.
|
||||
|
||||
=item <template type>
|
||||
|
||||
This is what comes after the template name but before C<.tmpl> in the
|
||||
template's path. In most cases this is C<html>, but sometimes it's
|
||||
C<none>, C<txt>, C<js>, or various other formats, indicating what
|
||||
type of output the template has.
|
||||
|
||||
=back
|
||||
|
||||
=head3 Adding New Template Hooks to Bugzilla
|
||||
|
||||
Adding new template hooks is just like adding code hooks (see
|
||||
L</Adding New Hooks To Bugzilla>) except that you don't have to
|
||||
document them, and including example code is optional.
|
||||
|
||||
=head2 Overriding Existing Templates
|
||||
|
||||
Sometimes you don't want to extend a template, you just want to replace
|
||||
it entirely with your extension's template, or you want to add an entirely
|
||||
new template to Bugzilla for your extension to use.
|
||||
|
||||
To replace the F<template/en/default/global/banner.html.tmpl> template
|
||||
in an extension named "Foo", create a file called
|
||||
F<extensions/Foo/template/en/default/global/banner.html.tmpl>. Note that this
|
||||
is very similar to the path for a template hook, except that it excludes
|
||||
F<hook/>, and the template is named I<exactly> like the standard Bugzilla
|
||||
template.
|
||||
|
||||
You can also use this method to add entirely new templates. If you have
|
||||
an extension named "Foo", and you add a file named
|
||||
F<extensions/Foo/template/en/default/foo/bar.html.tmpl>, you can load
|
||||
that in your code using C<< $template->process('foo/bar.html.tmpl') >>.
|
||||
|
||||
=head3 A Warning About Extensions That You Want To Distribute
|
||||
|
||||
You should never override an existing Bugzilla template in an
|
||||
extension that you plan to distribute to others, because only one extension
|
||||
can override any given template, and which extension will "win" that war
|
||||
if there are multiple extensions installed is totally undefined.
|
||||
|
||||
However, adding new templates in an extension that you want to distribute
|
||||
is fine, though you have to be careful about how you name them, because
|
||||
any templates with an identical path and name (say, both called
|
||||
F<global/stuff.html.tmpl>) will conflict. The usual way to work around
|
||||
this is to put all your custom templates into a template path that's
|
||||
named after your extension (since the name of your extension has to be
|
||||
unique anyway). So if your extension was named Foo, your custom templates
|
||||
would go into F<extensions/Foo/template/en/default/foo/>. The only
|
||||
time that doesn't work is with the C<page_before_template> extension, in which
|
||||
case your templates should probably be in a directory like
|
||||
F<extensions/Foo/template/en/default/page/foo/> so as not to conflict with
|
||||
other pages that other extensions might add.
|
||||
|
||||
=head2 CSS, JavaScript, and Images
|
||||
|
||||
If you include CSS, JavaScript, and images in your extension that are
|
||||
served directly to the user (that is, they're not read by a script and
|
||||
then printed--they're just linked directly in your HTML), they should go
|
||||
into the F<web/> subdirectory of your extension.
|
||||
|
||||
So, for example, if you had a CSS file called F<style.css> and your
|
||||
extension was called F<Foo>, your file would go into
|
||||
F<extensions/Foo/web/style.css>.
|
||||
|
||||
=head2 Disabling Your Extension
|
||||
|
||||
If you want your extension to be totally ignored by Bugzilla (it will
|
||||
not be compiled or seen to exist at all), then create a file called
|
||||
C<disabled> in your extension's directory. (If your extension is just
|
||||
a file, like F<extensions/Foo.pm>, you cannot use this method to disable
|
||||
your extension, and will just have to remove it from the directory if you
|
||||
want to totally disable it.) Note that if you are running under mod_perl,
|
||||
you may have to restart your web server for this to take effect.
|
||||
|
||||
If you want your extension to be compiled and have L<checksetup> check
|
||||
for its module pre-requisites, but you don't want the module to be used
|
||||
by Bugzilla, then you should make your extension's L</enabled> method
|
||||
return C<0> or some false value.
|
||||
|
||||
=head1 DISTRIBUTING EXTENSIONS
|
||||
|
||||
If you've made an extension and you want to publish it, the first
|
||||
thing you'll want to do is package up your extension's code and
|
||||
then put a link to it in the appropriate section of
|
||||
L<http://wiki.mozilla.org/Bugzilla:Addons>.
|
||||
|
||||
=head2 Distributing on CPAN
|
||||
|
||||
If you want a centralized distribution point that makes it easy
|
||||
for Bugzilla users to install your extension, it is possible to
|
||||
distribute your Bugzilla Extension through CPAN.
|
||||
|
||||
The details of making a standard CPAN module are too much to
|
||||
go into here, but a lot of it is covered in L<perlmodlib>
|
||||
and on L<http://www.cpan.org/> among other places.
|
||||
|
||||
When you distribute your extension via CPAN, your F<Extension.pm>
|
||||
should simply install itself as F<Bugzilla/Extension/Foo.pm>,
|
||||
where C<Foo> is the name of your module. You do not need a separate
|
||||
F<Config.pm> file, because CPAN itself will handle installing
|
||||
the prerequisites of your module, so Bugzilla doesn't have to
|
||||
worry about it.
|
||||
|
||||
=head3 Templates in extensions distributed on CPAN
|
||||
|
||||
If your extension is F</usr/lib/perl5/Bugzilla/Extension/Foo.pm>,
|
||||
then Bugzilla will look for templates in the directory
|
||||
F</usr/lib/perl5/Bugzilla/Extension/Foo/template/>.
|
||||
|
||||
You can change this behavior by overriding the L</template_dir>
|
||||
or L</package_dir> methods described lower down in this document.
|
||||
|
||||
=head3 Using an extension distributed on CPAN
|
||||
|
||||
There is a file named F<data/extensions/additional> in Bugzilla.
|
||||
This is a plain-text file. Each line is the name of a module,
|
||||
like C<Bugzilla::Extension::Foo>. In addition to the extensions
|
||||
in the F<extensions/> directory, each module listed in this file
|
||||
will be loaded as a Bugzilla Extension whenever Bugzilla loads or
|
||||
uses extensions.
|
||||
|
||||
=head1 GETTING HELP WITH WRITING EXTENSIONS
|
||||
|
||||
If you are an extension author and you'd like some assistance from other
|
||||
extension authors or the Bugzilla development team, you can use the
|
||||
normal support channels described at L<http://www.bugzilla.org/support/>.
|
||||
|
||||
=head1 ADDITIONAL CONSTANTS
|
||||
|
||||
In addition to C<NAME>, there are some other constants you might
|
||||
want to define:
|
||||
|
||||
=head2 C<$VERSION>
|
||||
|
||||
This should be a string that describes what version of your extension
|
||||
this is. Something like C<1.0>, C<1.3.4> or a similar string.
|
||||
|
||||
There are no particular restrictions on the format of version numbers,
|
||||
but you should probably keep them to just numbers and periods, in the
|
||||
interest of other software that parses version numbers.
|
||||
|
||||
By default, this will be C<undef> if you don't define it.
|
||||
|
||||
=head1 SUBCLASS METHODS
|
||||
|
||||
In addition to hooks, there are a few methods that your extension can
|
||||
define to modify its behavior, if you want:
|
||||
|
||||
=head2 Class Methods
|
||||
|
||||
These methods are called on your extension's class. (Like
|
||||
C<< Bugzilla::Extension::Foo->some_method >>).
|
||||
|
||||
=head3 C<new>
|
||||
|
||||
Once every request, this method is called on your extension in order
|
||||
to create an "instance" of it. (Extensions are treated like objects--they
|
||||
are instantiated once per request in Bugzilla, and then methods are
|
||||
called on the object.)
|
||||
|
||||
=head2 Instance Methods
|
||||
|
||||
These are called on an instantiated Extension object.
|
||||
|
||||
=head3 C<enabled>
|
||||
|
||||
This should return C<1> if this extension's hook code should be run
|
||||
by Bugzilla, and C<0> otherwise.
|
||||
|
||||
=head3 C<package_dir>
|
||||
|
||||
This returns the directory that your extension is located in.
|
||||
|
||||
If this is an extension that was installed via CPAN, the directory will
|
||||
be the path to F<Bugzilla/Extension/Foo/>, if C<Foo.pm> is the name of your
|
||||
extension.
|
||||
|
||||
If you want to override this method, and you have a F<Config.pm>, you must
|
||||
override this method in F<Config.pm>.
|
||||
|
||||
=head3 C<template_dir>
|
||||
|
||||
The directory that your package's templates are in.
|
||||
|
||||
This defaults to the C<template> subdirectory of the L</package_dir>.
|
||||
|
||||
If you want to override this method, and you have a F<Config.pm>, you must
|
||||
override this method in F<Config.pm>.
|
||||
|
||||
=head3 C<lib_dir>
|
||||
|
||||
The directory where your extension's libraries are.
|
||||
|
||||
This defaults to the C<lib> subdirectory of the L</package_dir>.
|
||||
|
||||
If you want to override this method, and you have a F<Config.pm>, you must
|
||||
override this method in F<Config.pm>.
|
||||
|
||||
=head1 BUGZILLA::EXTENSION CLASS METHODS
|
||||
|
||||
These are used internally by Bugzilla to load and set up extensions.
|
||||
If you are an extension author, you don't need to care about these.
|
||||
|
||||
=head2 C<load>
|
||||
|
||||
Takes two arguments, the path to F<Extension.pm> and the path to F<Config.pm>,
|
||||
for an extension. Loads the extension's code packages into memory using
|
||||
C<require>, does some sanity-checking on the extension, and returns the
|
||||
package name of the loaded extension.
|
||||
|
||||
=head2 C<load_all>
|
||||
|
||||
Calls L</load> for every enabled extension installed into Bugzilla,
|
||||
and returns an arrayref of all the package names that were loaded.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,347 +0,0 @@
|
||||
# -*- 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 Initial Developer of the Original Code is NASA.
|
||||
# Portions created by NASA are Copyright (C) 2006 San Jose State
|
||||
# University Foundation. All Rights Reserved.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::Field::Choice;
|
||||
|
||||
use base qw(Bugzilla::Field::ChoiceInterface Bugzilla::Object);
|
||||
|
||||
use Bugzilla::Config qw(SetParam write_params);
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Field;
|
||||
use Bugzilla::Util qw(trim detaint_natural);
|
||||
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
##################
|
||||
# Initialization #
|
||||
##################
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
id
|
||||
value
|
||||
sortkey
|
||||
isactive
|
||||
visibility_value_id
|
||||
);
|
||||
|
||||
use constant UPDATE_COLUMNS => qw(
|
||||
value
|
||||
sortkey
|
||||
isactive
|
||||
visibility_value_id
|
||||
);
|
||||
|
||||
use constant NAME_FIELD => 'value';
|
||||
use constant LIST_ORDER => 'sortkey, value';
|
||||
|
||||
use constant VALIDATORS => {
|
||||
value => \&_check_value,
|
||||
sortkey => \&_check_sortkey,
|
||||
visibility_value_id => \&_check_visibility_value_id,
|
||||
isactive => \&_check_isactive,
|
||||
};
|
||||
|
||||
use constant CLASS_MAP => {
|
||||
bug_status => 'Bugzilla::Status',
|
||||
classification => 'Bugzilla::Classification',
|
||||
component => 'Bugzilla::Component',
|
||||
product => 'Bugzilla::Product',
|
||||
};
|
||||
|
||||
use constant DEFAULT_MAP => {
|
||||
op_sys => 'defaultopsys',
|
||||
rep_platform => 'defaultplatform',
|
||||
priority => 'defaultpriority',
|
||||
bug_severity => 'defaultseverity',
|
||||
};
|
||||
|
||||
#################
|
||||
# Class Factory #
|
||||
#################
|
||||
|
||||
# Bugzilla::Field::Choice is actually an abstract base class. Every field
|
||||
# type has its own dynamically-generated class for its values. This allows
|
||||
# certain fields to have special types, like how bug_status's values
|
||||
# are Bugzilla::Status objects.
|
||||
|
||||
sub type {
|
||||
my ($class, $field) = @_;
|
||||
my $field_obj = blessed $field ? $field : Bugzilla::Field->check($field);
|
||||
my $field_name = $field_obj->name;
|
||||
|
||||
if ($class->CLASS_MAP->{$field_name}) {
|
||||
return $class->CLASS_MAP->{$field_name};
|
||||
}
|
||||
|
||||
# For generic classes, we use a lowercase class name, so as
|
||||
# not to interfere with any real subclasses we might make some day.
|
||||
my $package = "Bugzilla::Field::Choice::$field_name";
|
||||
Bugzilla->request_cache->{"field_$package"} = $field_obj;
|
||||
|
||||
# This package only needs to be created once. We check if the DB_TABLE
|
||||
# glob for this package already exists, which tells us whether or not
|
||||
# we need to create the package (this works even under mod_perl, where
|
||||
# this package definition will persist across requests)).
|
||||
if (!defined *{"${package}::DB_TABLE"}) {
|
||||
eval <<EOC;
|
||||
package $package;
|
||||
use base qw(Bugzilla::Field::Choice);
|
||||
use constant DB_TABLE => '$field_name';
|
||||
EOC
|
||||
}
|
||||
|
||||
return $package;
|
||||
}
|
||||
|
||||
################
|
||||
# Constructors #
|
||||
################
|
||||
|
||||
# We just make new() enforce this, which should give developers
|
||||
# the understanding that you can't use Bugzilla::Field::Choice
|
||||
# without calling type().
|
||||
sub new {
|
||||
my $class = shift;
|
||||
if ($class eq 'Bugzilla::Field::Choice') {
|
||||
ThrowCodeError('field_choice_must_use_type');
|
||||
}
|
||||
$class->SUPER::new(@_);
|
||||
}
|
||||
|
||||
#########################
|
||||
# Database Manipulation #
|
||||
#########################
|
||||
|
||||
# Our subclasses can take more arguments than we normally accept.
|
||||
# So, we override create() to remove arguments that aren't valid
|
||||
# columns. (Normally Bugzilla::Object dies if you pass arguments
|
||||
# that aren't valid columns.)
|
||||
sub create {
|
||||
my $class = shift;
|
||||
my ($params) = @_;
|
||||
foreach my $key (keys %$params) {
|
||||
if (!grep {$_ eq $key} $class->_get_db_columns) {
|
||||
delete $params->{$key};
|
||||
}
|
||||
}
|
||||
return $class->SUPER::create(@_);
|
||||
}
|
||||
|
||||
sub update {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $fname = $self->field->name;
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
my ($changes, $old_self) = $self->SUPER::update(@_);
|
||||
if (exists $changes->{value}) {
|
||||
my ($old, $new) = @{ $changes->{value} };
|
||||
if ($self->field->type == FIELD_TYPE_MULTI_SELECT) {
|
||||
$dbh->do("UPDATE bug_$fname SET value = ? WHERE value = ?",
|
||||
undef, $new, $old);
|
||||
}
|
||||
else {
|
||||
$dbh->do("UPDATE bugs SET $fname = ? WHERE $fname = ?",
|
||||
undef, $new, $old);
|
||||
}
|
||||
|
||||
if ($old_self->is_default) {
|
||||
my $param = $self->DEFAULT_MAP->{$self->field->name};
|
||||
SetParam($param, $self->name);
|
||||
write_params();
|
||||
}
|
||||
}
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
return wantarray ? ($changes, $old_self) : $changes;
|
||||
}
|
||||
|
||||
sub remove_from_db {
|
||||
my $self = shift;
|
||||
if ($self->is_default) {
|
||||
ThrowUserError('fieldvalue_is_default',
|
||||
{ field => $self->field, value => $self,
|
||||
param_name => $self->DEFAULT_MAP->{$self->field->name},
|
||||
});
|
||||
}
|
||||
if ($self->is_static) {
|
||||
ThrowUserError('fieldvalue_not_deletable',
|
||||
{ field => $self->field, value => $self });
|
||||
}
|
||||
if ($self->bug_count) {
|
||||
ThrowUserError("fieldvalue_still_has_bugs",
|
||||
{ field => $self->field, value => $self });
|
||||
}
|
||||
$self->_check_if_controller(); # From ChoiceInterface.
|
||||
$self->SUPER::remove_from_db();
|
||||
}
|
||||
|
||||
############
|
||||
# Mutators #
|
||||
############
|
||||
|
||||
sub set_is_active { $_[0]->set('isactive', $_[1]); }
|
||||
sub set_name { $_[0]->set('value', $_[1]); }
|
||||
sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
|
||||
sub set_visibility_value {
|
||||
my ($self, $value) = @_;
|
||||
$self->set('visibility_value_id', $value);
|
||||
delete $self->{visibility_value};
|
||||
}
|
||||
|
||||
##############
|
||||
# Validators #
|
||||
##############
|
||||
|
||||
sub _check_isactive {
|
||||
my ($invocant, $value) = @_;
|
||||
$value = Bugzilla::Object::check_boolean($invocant, $value);
|
||||
if (!$value and ref $invocant) {
|
||||
if ($invocant->is_default) {
|
||||
my $field = $invocant->field;
|
||||
ThrowUserError('fieldvalue_is_default',
|
||||
{ value => $invocant, field => $field,
|
||||
param_name => $invocant->DEFAULT_MAP->{$field->name}
|
||||
});
|
||||
}
|
||||
if ($invocant->is_static) {
|
||||
ThrowUserError('fieldvalue_not_deletable',
|
||||
{ value => $invocant, field => $invocant->field });
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
sub _check_value {
|
||||
my ($invocant, $value) = @_;
|
||||
|
||||
my $field = $invocant->field;
|
||||
|
||||
$value = trim($value);
|
||||
|
||||
# Make sure people don't rename static values
|
||||
if (blessed($invocant) && $value ne $invocant->name
|
||||
&& $invocant->is_static)
|
||||
{
|
||||
ThrowUserError('fieldvalue_not_editable',
|
||||
{ field => $field, old_value => $invocant });
|
||||
}
|
||||
|
||||
ThrowUserError('fieldvalue_undefined') if !defined $value || $value eq "";
|
||||
ThrowUserError('fieldvalue_name_too_long', { value => $value })
|
||||
if length($value) > MAX_FIELD_VALUE_SIZE;
|
||||
|
||||
my $exists = $invocant->type($field)->new({ name => $value });
|
||||
if ($exists && (!blessed($invocant) || $invocant->id != $exists->id)) {
|
||||
ThrowUserError('fieldvalue_already_exists',
|
||||
{ field => $field, value => $exists });
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
sub _check_sortkey {
|
||||
my ($invocant, $value) = @_;
|
||||
$value = trim($value);
|
||||
return 0 if !$value;
|
||||
# Store for the error message in case detaint_natural clears it.
|
||||
my $orig_value = $value;
|
||||
detaint_natural($value)
|
||||
|| ThrowUserError('fieldvalue_sortkey_invalid',
|
||||
{ sortkey => $orig_value,
|
||||
field => $invocant->field });
|
||||
return $value;
|
||||
}
|
||||
|
||||
sub _check_visibility_value_id {
|
||||
my ($invocant, $value_id) = @_;
|
||||
$value_id = trim($value_id);
|
||||
my $field = $invocant->field->value_field;
|
||||
return undef if !$field || !$value_id;
|
||||
my $value_obj = Bugzilla::Field::Choice->type($field)
|
||||
->check({ id => $value_id });
|
||||
return $value_obj->id;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Field::Choice - A legal value for a <select>-type field.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $field = new Bugzilla::Field({name => 'bug_status'});
|
||||
|
||||
my $choice = new Bugzilla::Field::Choice->type($field)->new(1);
|
||||
|
||||
my $choices = Bugzilla::Field::Choice->type($field)->new_from_list([1,2,3]);
|
||||
my $choices = Bugzilla::Field::Choice->type($field)->get_all();
|
||||
my $choices = Bugzilla::Field::Choice->type($field->match({ sortkey => 10 });
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is an implementation of L<Bugzilla::Object>, but with a twist.
|
||||
You can't call any class methods (such as C<new>, C<create>, etc.)
|
||||
directly on C<Bugzilla::Field::Choice> itself. Instead, you have to
|
||||
call C<Bugzilla::Field::Choice-E<gt>type($field)> to get the class
|
||||
you're going to instantiate, and then you call the methods on that.
|
||||
|
||||
We do that because each field has its own database table for its values, so
|
||||
each value type needs its own class.
|
||||
|
||||
See the L</SYNOPSIS> for examples of how this works.
|
||||
|
||||
This class implements L<Bugzilla::Field::ChoiceInterface>, and so all
|
||||
methods of that class are also available here.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 Class Factory
|
||||
|
||||
In object-oriented design, a "class factory" is a method that picks
|
||||
and returns the right class for you, based on an argument that you pass.
|
||||
|
||||
=over
|
||||
|
||||
=item C<type>
|
||||
|
||||
Takes a single argument, which is either the name of a field from the
|
||||
C<fielddefs> table, or a L<Bugzilla::Field> object representing a field.
|
||||
|
||||
Returns an appropriate subclass of C<Bugzilla::Field::Choice> that you
|
||||
can now call class methods on (like C<new>, C<create>, C<match>, etc.)
|
||||
|
||||
B<NOTE>: YOU CANNOT CALL CLASS METHODS ON C<Bugzilla::Field::Choice>. You
|
||||
must call C<type> to get a class you can call methods on.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Mutators
|
||||
|
||||
This class implements mutators for all of the settable accessors in
|
||||
L<Bugzilla::Field::ChoiceInterface>.
|
||||
@@ -1,273 +0,0 @@
|
||||
# -*- 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 Initial Developer of the Original Code is NASA.
|
||||
# Portions created by NASA are Copyright (C) 2006 San Jose State
|
||||
# University Foundation. All Rights Reserved.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::Field::ChoiceInterface;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Field;
|
||||
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
# Helps implement the "field" accessor without subclasses having to
|
||||
# write code.
|
||||
sub FIELD_NAME { return $_[0]->DB_TABLE; }
|
||||
|
||||
####################
|
||||
# Subclass Helpers #
|
||||
####################
|
||||
|
||||
sub _check_if_controller {
|
||||
my $self = shift;
|
||||
my $vis_fields = $self->controls_visibility_of_fields;
|
||||
my $values = $self->controlled_values_array;
|
||||
if (@$vis_fields || @$values) {
|
||||
ThrowUserError('fieldvalue_is_controller',
|
||||
{ value => $self, fields => [map($_->name, @$vis_fields)],
|
||||
vals => $self->controlled_values });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#############
|
||||
# Accessors #
|
||||
#############
|
||||
|
||||
sub is_active { return $_[0]->{'isactive'}; }
|
||||
sub sortkey { return $_[0]->{'sortkey'}; }
|
||||
|
||||
sub bug_count {
|
||||
my $self = shift;
|
||||
return $self->{bug_count} if defined $self->{bug_count};
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $fname = $self->field->name;
|
||||
my $count;
|
||||
if ($self->field->type == FIELD_TYPE_MULTI_SELECT) {
|
||||
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM bug_$fname
|
||||
WHERE value = ?", undef, $self->name);
|
||||
}
|
||||
else {
|
||||
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs
|
||||
WHERE $fname = ?",
|
||||
undef, $self->name);
|
||||
}
|
||||
$self->{bug_count} = $count;
|
||||
return $count;
|
||||
}
|
||||
|
||||
sub field {
|
||||
my $invocant = shift;
|
||||
my $class = ref $invocant || $invocant;
|
||||
my $cache = Bugzilla->request_cache;
|
||||
# This is just to make life easier for subclasses. Our auto-generated
|
||||
# subclasses from Bugzilla::Field::Choice->type() already have this set.
|
||||
$cache->{"field_$class"} ||=
|
||||
new Bugzilla::Field({ name => $class->FIELD_NAME });
|
||||
return $cache->{"field_$class"};
|
||||
}
|
||||
|
||||
sub is_default {
|
||||
my $self = shift;
|
||||
my $name = $self->DEFAULT_MAP->{$self->field->name};
|
||||
# If it doesn't exist in DEFAULT_MAP, then there is no parameter
|
||||
# related to this field.
|
||||
return 0 unless $name;
|
||||
return ($self->name eq Bugzilla->params->{$name}) ? 1 : 0;
|
||||
}
|
||||
|
||||
sub is_static {
|
||||
my $self = shift;
|
||||
# If we need to special-case Resolution for *anything* else, it should
|
||||
# get its own subclass.
|
||||
if ($self->field->name eq 'resolution') {
|
||||
return grep($_ eq $self->name, ('', 'FIXED', 'DUPLICATE'))
|
||||
? 1 : 0;
|
||||
}
|
||||
elsif ($self->field->custom) {
|
||||
return $self->name eq '---' ? 1 : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub controls_visibility_of_fields {
|
||||
my $self = shift;
|
||||
$self->{controls_visibility_of_fields} ||= Bugzilla::Field->match(
|
||||
{ visibility_field_id => $self->field->id,
|
||||
visibility_value_id => $self->id });
|
||||
return $self->{controls_visibility_of_fields};
|
||||
}
|
||||
|
||||
sub visibility_value {
|
||||
my $self = shift;
|
||||
if ($self->{visibility_value_id}) {
|
||||
require Bugzilla::Field::Choice;
|
||||
$self->{visibility_value} ||=
|
||||
Bugzilla::Field::Choice->type($self->field->value_field)->new(
|
||||
$self->{visibility_value_id});
|
||||
}
|
||||
return $self->{visibility_value};
|
||||
}
|
||||
|
||||
sub controlled_values {
|
||||
my $self = shift;
|
||||
return $self->{controlled_values} if defined $self->{controlled_values};
|
||||
my $fields = $self->field->controls_values_of;
|
||||
my %controlled_values;
|
||||
require Bugzilla::Field::Choice;
|
||||
foreach my $field (@$fields) {
|
||||
$controlled_values{$field->name} =
|
||||
Bugzilla::Field::Choice->type($field)
|
||||
->match({ visibility_value_id => $self->id });
|
||||
}
|
||||
$self->{controlled_values} = \%controlled_values;
|
||||
return $self->{controlled_values};
|
||||
}
|
||||
|
||||
sub controlled_values_array {
|
||||
my ($self) = @_;
|
||||
my $values = $self->controlled_values;
|
||||
return [map { @{ $values->{$_} } } keys %$values];
|
||||
}
|
||||
|
||||
sub is_visible_on_bug {
|
||||
my ($self, $bug) = @_;
|
||||
|
||||
# Values currently set on the bug are always shown.
|
||||
return 1 if $self->is_set_on_bug($bug);
|
||||
|
||||
# Inactive values are, otherwise, never shown.
|
||||
return 0 if !$self->is_active;
|
||||
|
||||
# Values without a visibility value are, otherwise, always shown.
|
||||
my $visibility_value = $self->visibility_value;
|
||||
return 1 if !$visibility_value;
|
||||
|
||||
# Values with a visibility value are only shown if the visibility
|
||||
# value is set on the bug.
|
||||
return $visibility_value->is_set_on_bug($bug);
|
||||
}
|
||||
|
||||
sub is_set_on_bug {
|
||||
my ($self, $bug) = @_;
|
||||
my $field_name = $self->FIELD_NAME;
|
||||
# This allows bug/create/create.html.tmpl to pass in a hashref that
|
||||
# looks like a bug object.
|
||||
my $value = blessed($bug) ? $bug->$field_name : $bug->{$field_name};
|
||||
return 0 if !defined $value;
|
||||
|
||||
if ($self->field->type == FIELD_TYPE_BUG_URLS
|
||||
or $self->field->type == FIELD_TYPE_MULTI_SELECT)
|
||||
{
|
||||
return grep($_ eq $self->name, @$value) ? 1 : 0;
|
||||
}
|
||||
return $value eq $self->name ? 1 : 0;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Field::ChoiceInterface - Makes an object act like a
|
||||
Bugzilla::Field::Choice.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is an "interface", in the Java sense (sometimes called a "Role"
|
||||
or a "Mixin" in other languages). L<Bugzilla::Field::Choice> is the
|
||||
primary implementor of this interface, but other classes also implement
|
||||
it if they want to "act like" L<Bugzilla::Field::Choice>.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 Accessors
|
||||
|
||||
These are in addition to the standard L<Bugzilla::Object> accessors.
|
||||
|
||||
=over
|
||||
|
||||
=item C<sortkey>
|
||||
|
||||
The key that determines the sort order of this item.
|
||||
|
||||
=item C<field>
|
||||
|
||||
The L<Bugzilla::Field> object that this field value belongs to.
|
||||
|
||||
=item C<is_active>
|
||||
|
||||
Whether or not this value should appear as an option on bugs that do
|
||||
not already have it set as the current value.
|
||||
|
||||
=item C<is_static>
|
||||
|
||||
C<0> if this field value can be renamed or deleted, C<1> otherwise.
|
||||
|
||||
=item C<is_default>
|
||||
|
||||
C<1> if this is the default value for this field, C<0> otherwise.
|
||||
|
||||
=item C<bug_count>
|
||||
|
||||
An integer count of the number of bugs that have this value set.
|
||||
|
||||
=item C<controls_visibility_of_fields>
|
||||
|
||||
Returns an arrayref of L<Bugzilla::Field> objects, representing any
|
||||
fields whose visibility are controlled by this field value.
|
||||
|
||||
=item C<controlled_values>
|
||||
|
||||
Tells you which values in B<other> fields appear (become visible) when this
|
||||
value is set in its field.
|
||||
|
||||
Returns a hashref of arrayrefs. The hash keys are the names of fields,
|
||||
and the values are arrays of objects that implement
|
||||
C<Bugzilla::Field::ChoiceInterface>, representing values that this value
|
||||
controls the visibility of, for that field.
|
||||
|
||||
=item C<visibility_value>
|
||||
|
||||
Returns an object that implements C<Bugzilla::Field::ChoiceInterface>,
|
||||
which represents the value that needs to be set in order for this
|
||||
value to appear in the UI.
|
||||
|
||||
=item C<is_visible_on_bug>
|
||||
|
||||
Returns C<1> if, according to the settings of C<is_active> and
|
||||
C<visibility_value>, this value should be displayed as an option
|
||||
when viewing a bug. Returns C<0> otherwise.
|
||||
|
||||
Takes a single argument, a L<Bugzilla::Bug> object or a hash with
|
||||
similar fields to a L<Bugzilla::Bug> object.
|
||||
|
||||
=item C<is_set_on_bug>
|
||||
|
||||
Returns C<1> if this value is the current value set for its field on
|
||||
the passed-in L<Bugzilla::Bug> object (or a hash that looks like a
|
||||
L<Bugzilla::Bug>). For multi-valued fields, we return C<1> if
|
||||
I<any> of the currently selected values are this value.
|
||||
|
||||
Returns C<0> otherwise.
|
||||
|
||||
=back
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,509 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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>
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::FlagType;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::FlagType - A module to deal with Bugzilla flag types.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
FlagType.pm provides an interface to flag types as stored in Bugzilla.
|
||||
See below for more information.
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
|
||||
Use of private functions/variables outside this module may lead to
|
||||
unexpected results after an upgrade. Please avoid using private
|
||||
functions in other files/modules. Private functions are functions
|
||||
whose names start with _ or are specifically noted as being private.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Group;
|
||||
|
||||
use base qw(Bugzilla::Object);
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
=begin private
|
||||
|
||||
=head1 PRIVATE VARIABLES/CONSTANTS
|
||||
|
||||
=over
|
||||
|
||||
=item C<DB_COLUMNS>
|
||||
|
||||
basic sets of columns and tables for getting flag types from the
|
||||
database.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
flagtypes.id
|
||||
flagtypes.name
|
||||
flagtypes.description
|
||||
flagtypes.cc_list
|
||||
flagtypes.target_type
|
||||
flagtypes.sortkey
|
||||
flagtypes.is_active
|
||||
flagtypes.is_requestable
|
||||
flagtypes.is_requesteeble
|
||||
flagtypes.is_multiplicable
|
||||
flagtypes.grant_group_id
|
||||
flagtypes.request_group_id
|
||||
);
|
||||
|
||||
=pod
|
||||
|
||||
=over
|
||||
|
||||
=item C<DB_TABLE>
|
||||
|
||||
Which database(s) is the data coming from?
|
||||
|
||||
Note: when adding tables to DB_TABLE, make sure to include the separator
|
||||
(i.e. words like "LEFT OUTER JOIN") before the table name, since tables take
|
||||
multiple separators based on the join type, and therefore it is not possible
|
||||
to join them later using a single known separator.
|
||||
|
||||
=back
|
||||
|
||||
=end private
|
||||
|
||||
=cut
|
||||
|
||||
use constant DB_TABLE => 'flagtypes';
|
||||
use constant LIST_ORDER => 'flagtypes.sortkey, flagtypes.name';
|
||||
|
||||
###############################
|
||||
#### Accessors ######
|
||||
###############################
|
||||
|
||||
=head2 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<id>
|
||||
|
||||
Returns the ID of the flagtype.
|
||||
|
||||
=item C<name>
|
||||
|
||||
Returns the name of the flagtype.
|
||||
|
||||
=item C<description>
|
||||
|
||||
Returns the description of the flagtype.
|
||||
|
||||
=item C<cc_list>
|
||||
|
||||
Returns the concatenated CC list for the flagtype, as a single string.
|
||||
|
||||
=item C<target_type>
|
||||
|
||||
Returns whether the flagtype applies to bugs or attachments.
|
||||
|
||||
=item C<is_active>
|
||||
|
||||
Returns whether the flagtype is active or disabled. Flags being
|
||||
in a disabled flagtype are not deleted. It only prevents you from
|
||||
adding new flags to it.
|
||||
|
||||
=item C<is_requestable>
|
||||
|
||||
Returns whether you can request for the given flagtype
|
||||
(i.e. whether the '?' flag is available or not).
|
||||
|
||||
=item C<is_requesteeble>
|
||||
|
||||
Returns whether you can ask someone specifically or not.
|
||||
|
||||
=item C<is_multiplicable>
|
||||
|
||||
Returns whether you can have more than one flag for the given
|
||||
flagtype in a given bug/attachment.
|
||||
|
||||
=item C<sortkey>
|
||||
|
||||
Returns the sortkey of the flagtype.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub id { return $_[0]->{'id'}; }
|
||||
sub name { return $_[0]->{'name'}; }
|
||||
sub description { return $_[0]->{'description'}; }
|
||||
sub cc_list { return $_[0]->{'cc_list'}; }
|
||||
sub target_type { return $_[0]->{'target_type'} eq 'b' ? 'bug' : 'attachment'; }
|
||||
sub is_active { return $_[0]->{'is_active'}; }
|
||||
sub is_requestable { return $_[0]->{'is_requestable'}; }
|
||||
sub is_requesteeble { return $_[0]->{'is_requesteeble'}; }
|
||||
sub is_multiplicable { return $_[0]->{'is_multiplicable'}; }
|
||||
sub sortkey { return $_[0]->{'sortkey'}; }
|
||||
sub request_group_id { return $_[0]->{'request_group_id'}; }
|
||||
sub grant_group_id { return $_[0]->{'grant_group_id'}; }
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
=pod
|
||||
|
||||
=over
|
||||
|
||||
=item C<grant_list>
|
||||
|
||||
Returns a reference to an array of users who have permission to grant this flag type.
|
||||
The arrays are populated with hashrefs containing the login, identity and visibility of users.
|
||||
|
||||
=item C<grant_group>
|
||||
|
||||
Returns the group (as a Bugzilla::Group object) in which a user
|
||||
must be in order to grant or deny a request.
|
||||
|
||||
=item C<request_group>
|
||||
|
||||
Returns the group (as a Bugzilla::Group object) in which a user
|
||||
must be in order to request or clear a flag.
|
||||
|
||||
=item C<flag_count>
|
||||
|
||||
Returns the number of flags belonging to the flagtype.
|
||||
|
||||
=item C<inclusions>
|
||||
|
||||
Return a hash of product/component IDs and names
|
||||
explicitly associated with the flagtype.
|
||||
|
||||
=item C<exclusions>
|
||||
|
||||
Return a hash of product/component IDs and names
|
||||
explicitly excluded from the flagtype.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub grant_list {
|
||||
my $self = shift;
|
||||
require Bugzilla::User;
|
||||
my @custusers;
|
||||
my @allusers = @{Bugzilla->user->get_userlist};
|
||||
foreach my $user (@allusers) {
|
||||
my $user_obj = new Bugzilla::User({name => $user->{login}});
|
||||
push(@custusers, $user) if $user_obj->can_set_flag($self);
|
||||
}
|
||||
return \@custusers;
|
||||
}
|
||||
|
||||
sub grant_group {
|
||||
my $self = shift;
|
||||
|
||||
if (!defined $self->{'grant_group'} && $self->{'grant_group_id'}) {
|
||||
$self->{'grant_group'} = new Bugzilla::Group($self->{'grant_group_id'});
|
||||
}
|
||||
return $self->{'grant_group'};
|
||||
}
|
||||
|
||||
sub request_group {
|
||||
my $self = shift;
|
||||
|
||||
if (!defined $self->{'request_group'} && $self->{'request_group_id'}) {
|
||||
$self->{'request_group'} = new Bugzilla::Group($self->{'request_group_id'});
|
||||
}
|
||||
return $self->{'request_group'};
|
||||
}
|
||||
|
||||
sub flag_count {
|
||||
my $self = shift;
|
||||
|
||||
if (!defined $self->{'flag_count'}) {
|
||||
$self->{'flag_count'} =
|
||||
Bugzilla->dbh->selectrow_array('SELECT COUNT(*) FROM flags
|
||||
WHERE type_id = ?', undef, $self->{'id'});
|
||||
}
|
||||
return $self->{'flag_count'};
|
||||
}
|
||||
|
||||
sub inclusions {
|
||||
my $self = shift;
|
||||
|
||||
if (!defined $self->{inclusions}) {
|
||||
($self->{inclusions}, $self->{inclusions_as_hash}) = get_clusions($self->id, 'in');
|
||||
}
|
||||
return $self->{inclusions};
|
||||
}
|
||||
|
||||
sub inclusions_as_hash {
|
||||
my $self = shift;
|
||||
|
||||
$self->inclusions unless defined $self->{inclusions_as_hash};
|
||||
return $self->{inclusions_as_hash};
|
||||
}
|
||||
|
||||
sub exclusions {
|
||||
my $self = shift;
|
||||
|
||||
if (!defined $self->{exclusions}) {
|
||||
($self->{exclusions}, $self->{exclusions_as_hash}) = get_clusions($self->id, 'ex');
|
||||
}
|
||||
return $self->{exclusions};
|
||||
}
|
||||
|
||||
sub exclusions_as_hash {
|
||||
my $self = shift;
|
||||
|
||||
$self->exclusions unless defined $self->{exclusions_as_hash};
|
||||
return $self->{exclusions_as_hash};
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Public Functions
|
||||
######################################################################
|
||||
|
||||
=pod
|
||||
|
||||
=head1 PUBLIC FUNCTIONS/METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<get_clusions($id, $type)>
|
||||
|
||||
Return a hash of product/component IDs and names
|
||||
associated with the flagtype:
|
||||
$clusions{'product_name:component_name'} = "product_ID:component_ID"
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub get_clusions {
|
||||
my ($id, $type) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $list =
|
||||
$dbh->selectall_arrayref("SELECT products.id, products.name, " .
|
||||
" components.id, components.name " .
|
||||
"FROM flagtypes, flag${type}clusions " .
|
||||
"LEFT OUTER JOIN products " .
|
||||
" ON flag${type}clusions.product_id = products.id " .
|
||||
"LEFT OUTER JOIN components " .
|
||||
" ON flag${type}clusions.component_id = components.id " .
|
||||
"WHERE flagtypes.id = ? " .
|
||||
" AND flag${type}clusions.type_id = flagtypes.id",
|
||||
undef, $id);
|
||||
my (%clusions, %clusions_as_hash);
|
||||
foreach my $data (@$list) {
|
||||
my ($product_id, $product_name, $component_id, $component_name) = @$data;
|
||||
$product_id ||= 0;
|
||||
$product_name ||= "__Any__";
|
||||
$component_id ||= 0;
|
||||
$component_name ||= "__Any__";
|
||||
$clusions{"$product_name:$component_name"} = "$product_id:$component_id";
|
||||
$clusions_as_hash{$product_id}->{$component_id} = 1;
|
||||
}
|
||||
return (\%clusions, \%clusions_as_hash);
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=over
|
||||
|
||||
=item C<match($criteria)>
|
||||
|
||||
Queries the database for flag types matching the given criteria
|
||||
and returns a list of matching flagtype objects.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub match {
|
||||
my ($criteria) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# Depending on the criteria, we may have to append additional tables.
|
||||
my $tables = [DB_TABLE];
|
||||
my @criteria = sqlify_criteria($criteria, $tables);
|
||||
$tables = join(' ', @$tables);
|
||||
$criteria = join(' AND ', @criteria);
|
||||
|
||||
my $flagtype_ids = $dbh->selectcol_arrayref("SELECT id FROM $tables WHERE $criteria");
|
||||
|
||||
return Bugzilla::FlagType->new_from_list($flagtype_ids);
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=over
|
||||
|
||||
=item C<count($criteria)>
|
||||
|
||||
Returns the total number of flag types matching the given criteria.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub count {
|
||||
my ($criteria) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# Depending on the criteria, we may have to append additional tables.
|
||||
my $tables = [DB_TABLE];
|
||||
my @criteria = sqlify_criteria($criteria, $tables);
|
||||
$tables = join(' ', @$tables);
|
||||
$criteria = join(' AND ', @criteria);
|
||||
|
||||
my $count = $dbh->selectrow_array("SELECT COUNT(flagtypes.id)
|
||||
FROM $tables WHERE $criteria");
|
||||
return $count;
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Private Functions
|
||||
######################################################################
|
||||
|
||||
=begin private
|
||||
|
||||
=head1 PRIVATE FUNCTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item C<sqlify_criteria($criteria, $tables)>
|
||||
|
||||
Converts a hash of criteria into a list of SQL criteria.
|
||||
$criteria is a reference to the criteria (field => value),
|
||||
$tables is a reference to an array of tables being accessed
|
||||
by the query.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub sqlify_criteria {
|
||||
my ($criteria, $tables) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# the generated list of SQL criteria; "1=1" is a clever way of making sure
|
||||
# there's something in the list so calling code doesn't have to check list
|
||||
# size before building a WHERE clause out of it
|
||||
my @criteria = ("1=1");
|
||||
|
||||
if ($criteria->{name}) {
|
||||
my $name = $dbh->quote($criteria->{name});
|
||||
trick_taint($name); # Detaint data as we have quoted it.
|
||||
push(@criteria, "flagtypes.name = $name");
|
||||
}
|
||||
if ($criteria->{target_type}) {
|
||||
# The target type is stored in the database as a one-character string
|
||||
# ("a" for attachment and "b" for bug), but this function takes complete
|
||||
# names ("attachment" and "bug") for clarity, so we must convert them.
|
||||
my $target_type = $criteria->{target_type} eq 'bug'? 'b' : 'a';
|
||||
push(@criteria, "flagtypes.target_type = '$target_type'");
|
||||
}
|
||||
if (exists($criteria->{is_active})) {
|
||||
my $is_active = $criteria->{is_active} ? "1" : "0";
|
||||
push(@criteria, "flagtypes.is_active = $is_active");
|
||||
}
|
||||
if ($criteria->{product_id}) {
|
||||
my $product_id = $criteria->{product_id};
|
||||
|
||||
# Add inclusions to the query, which simply involves joining the table
|
||||
# by flag type ID and target product/component.
|
||||
push(@$tables, "INNER JOIN flaginclusions AS i ON flagtypes.id = i.type_id");
|
||||
push(@criteria, "(i.product_id = $product_id OR i.product_id IS NULL)");
|
||||
|
||||
# Add exclusions to the query, which is more complicated. First of all,
|
||||
# we do a LEFT JOIN so we don't miss flag types with no exclusions.
|
||||
# Then, as with inclusions, we join on flag type ID and target product/
|
||||
# component. However, since we want flag types that *aren't* on the
|
||||
# exclusions list, we add a WHERE criteria to use only records with
|
||||
# NULL exclusion type, i.e. without any exclusions.
|
||||
my $join_clause = "flagtypes.id = e.type_id ";
|
||||
|
||||
my $addl_join_clause = "";
|
||||
if ($criteria->{component_id}) {
|
||||
my $component_id = $criteria->{component_id};
|
||||
push(@criteria, "(i.component_id = $component_id OR i.component_id IS NULL)");
|
||||
$join_clause .= "AND (e.component_id = $component_id OR e.component_id IS NULL) ";
|
||||
}
|
||||
else {
|
||||
$addl_join_clause = "AND e.component_id IS NULL OR (i.component_id != e.component_id) ";
|
||||
}
|
||||
$join_clause .= "AND ((e.product_id = $product_id $addl_join_clause) OR e.product_id IS NULL)";
|
||||
|
||||
push(@$tables, "LEFT JOIN flagexclusions AS e ON ($join_clause)");
|
||||
push(@criteria, "e.type_id IS NULL");
|
||||
}
|
||||
if ($criteria->{group}) {
|
||||
my $gid = $criteria->{group};
|
||||
detaint_natural($gid);
|
||||
push(@criteria, "(flagtypes.grant_group_id = $gid " .
|
||||
" OR flagtypes.request_group_id = $gid)");
|
||||
}
|
||||
|
||||
return @criteria;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=end private
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
=over
|
||||
|
||||
=item B<Bugzilla::Flags>
|
||||
|
||||
=back
|
||||
|
||||
=head1 CONTRIBUTORS
|
||||
|
||||
=over
|
||||
|
||||
=item Myk Melez <myk@mozilla.org>
|
||||
|
||||
=item Kevin Benton <kevin.benton@amd.com>
|
||||
|
||||
=item Frédéric Buclin <LpSolit@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
@@ -1,645 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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): Joel Peshkin <bugreport@peshkin.net>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Tiago R. Mello <timello@async.com.br>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::Group;
|
||||
|
||||
use base qw(Bugzilla::Object);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Config qw(:admin);
|
||||
|
||||
###############################
|
||||
##### Module Initialization ###
|
||||
###############################
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
groups.id
|
||||
groups.name
|
||||
groups.description
|
||||
groups.isbuggroup
|
||||
groups.userregexp
|
||||
groups.isactive
|
||||
groups.icon_url
|
||||
);
|
||||
|
||||
use constant DB_TABLE => 'groups';
|
||||
|
||||
use constant LIST_ORDER => 'isbuggroup, name';
|
||||
|
||||
use constant VALIDATORS => {
|
||||
name => \&_check_name,
|
||||
description => \&_check_description,
|
||||
userregexp => \&_check_user_regexp,
|
||||
isactive => \&_check_is_active,
|
||||
isbuggroup => \&_check_is_bug_group,
|
||||
icon_url => \&_check_icon_url,
|
||||
};
|
||||
|
||||
use constant UPDATE_COLUMNS => qw(
|
||||
name
|
||||
description
|
||||
userregexp
|
||||
isactive
|
||||
icon_url
|
||||
);
|
||||
|
||||
# Parameters that are lists of groups.
|
||||
use constant GROUP_PARAMS => qw(chartgroup insidergroup timetrackinggroup
|
||||
querysharegroup);
|
||||
|
||||
###############################
|
||||
#### Accessors ######
|
||||
###############################
|
||||
|
||||
sub description { return $_[0]->{'description'}; }
|
||||
sub is_bug_group { return $_[0]->{'isbuggroup'}; }
|
||||
sub user_regexp { return $_[0]->{'userregexp'}; }
|
||||
sub is_active { return $_[0]->{'isactive'}; }
|
||||
sub icon_url { return $_[0]->{'icon_url'}; }
|
||||
|
||||
sub bugs {
|
||||
my $self = shift;
|
||||
return $self->{bugs} if exists $self->{bugs};
|
||||
my $bug_ids = Bugzilla->dbh->selectcol_arrayref(
|
||||
'SELECT bug_id FROM bug_group_map WHERE group_id = ?',
|
||||
undef, $self->id);
|
||||
require Bugzilla::Bug;
|
||||
$self->{bugs} = Bugzilla::Bug->new_from_list($bug_ids);
|
||||
return $self->{bugs};
|
||||
}
|
||||
|
||||
sub members_direct {
|
||||
my ($self) = @_;
|
||||
$self->{members_direct} ||= $self->_get_members(GRANT_DIRECT);
|
||||
return $self->{members_direct};
|
||||
}
|
||||
|
||||
sub members_non_inherited {
|
||||
my ($self) = @_;
|
||||
$self->{members_non_inherited} ||= $self->_get_members();
|
||||
return $self->{members_non_inherited};
|
||||
}
|
||||
|
||||
# A helper for members_direct and members_non_inherited
|
||||
sub _get_members {
|
||||
my ($self, $grant_type) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $grant_clause = $grant_type ? "AND grant_type = $grant_type" : "";
|
||||
my $user_ids = $dbh->selectcol_arrayref(
|
||||
"SELECT DISTINCT user_id
|
||||
FROM user_group_map
|
||||
WHERE isbless = 0 $grant_clause AND group_id = ?", undef, $self->id);
|
||||
require Bugzilla::User;
|
||||
return Bugzilla::User->new_from_list($user_ids);
|
||||
}
|
||||
|
||||
sub flag_types {
|
||||
my $self = shift;
|
||||
require Bugzilla::FlagType;
|
||||
$self->{flag_types} ||= Bugzilla::FlagType::match({ group => $self->id });
|
||||
return $self->{flag_types};
|
||||
}
|
||||
|
||||
sub grant_direct {
|
||||
my ($self, $type) = @_;
|
||||
$self->{grant_direct} ||= {};
|
||||
return $self->{grant_direct}->{$type}
|
||||
if defined $self->{grant_direct}->{$type};
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $ids = $dbh->selectcol_arrayref(
|
||||
"SELECT member_id FROM group_group_map
|
||||
WHERE grantor_id = ? AND grant_type = $type",
|
||||
undef, $self->id) || [];
|
||||
|
||||
$self->{grant_direct}->{$type} = $self->new_from_list($ids);
|
||||
return $self->{grant_direct}->{$type};
|
||||
}
|
||||
|
||||
sub granted_by_direct {
|
||||
my ($self, $type) = @_;
|
||||
$self->{granted_by_direct} ||= {};
|
||||
return $self->{granted_by_direct}->{$type}
|
||||
if defined $self->{granted_by_direct}->{$type};
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $ids = $dbh->selectcol_arrayref(
|
||||
"SELECT grantor_id FROM group_group_map
|
||||
WHERE member_id = ? AND grant_type = $type",
|
||||
undef, $self->id) || [];
|
||||
|
||||
$self->{granted_by_direct}->{$type} = $self->new_from_list($ids);
|
||||
return $self->{granted_by_direct}->{$type};
|
||||
}
|
||||
|
||||
sub products {
|
||||
my $self = shift;
|
||||
return $self->{products} if exists $self->{products};
|
||||
my $product_data = Bugzilla->dbh->selectall_arrayref(
|
||||
'SELECT product_id, entry, membercontrol, othercontrol,
|
||||
canedit, editcomponents, editbugs, canconfirm
|
||||
FROM group_control_map WHERE group_id = ?', {Slice=>{}},
|
||||
$self->id);
|
||||
my @ids = map { $_->{product_id} } @$product_data;
|
||||
require Bugzilla::Product;
|
||||
my $products = Bugzilla::Product->new_from_list(\@ids);
|
||||
my %data_map = map { $_->{product_id} => $_ } @$product_data;
|
||||
my @retval;
|
||||
foreach my $product (@$products) {
|
||||
# Data doesn't need to contain product_id--we already have
|
||||
# the product object.
|
||||
delete $data_map{$product->id}->{product_id};
|
||||
push(@retval, { controls => $data_map{$product->id},
|
||||
product => $product });
|
||||
}
|
||||
$self->{products} = \@retval;
|
||||
return $self->{products};
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
sub set_description { $_[0]->set('description', $_[1]); }
|
||||
sub set_is_active { $_[0]->set('isactive', $_[1]); }
|
||||
sub set_name { $_[0]->set('name', $_[1]); }
|
||||
sub set_user_regexp { $_[0]->set('userregexp', $_[1]); }
|
||||
sub set_icon_url { $_[0]->set('icon_url', $_[1]); }
|
||||
|
||||
sub update {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->bz_start_transaction();
|
||||
my $changes = $self->SUPER::update(@_);
|
||||
|
||||
if (exists $changes->{name}) {
|
||||
my ($old_name, $new_name) = @{$changes->{name}};
|
||||
my $update_params;
|
||||
foreach my $group (GROUP_PARAMS) {
|
||||
if ($old_name eq Bugzilla->params->{$group}) {
|
||||
SetParam($group, $new_name);
|
||||
$update_params = 1;
|
||||
}
|
||||
}
|
||||
write_params() if $update_params;
|
||||
}
|
||||
|
||||
# If we've changed this group to be active, fix any Mandatory groups.
|
||||
$self->_enforce_mandatory if (exists $changes->{isactive}
|
||||
&& $changes->{isactive}->[1]);
|
||||
|
||||
$self->_rederive_regexp() if exists $changes->{userregexp};
|
||||
|
||||
Bugzilla::Hook::process('group_end_of_update',
|
||||
{ group => $self, changes => $changes });
|
||||
$dbh->bz_commit_transaction();
|
||||
return $changes;
|
||||
}
|
||||
|
||||
sub check_remove {
|
||||
my ($self, $params) = @_;
|
||||
|
||||
# System groups cannot be deleted!
|
||||
if (!$self->is_bug_group) {
|
||||
ThrowUserError("system_group_not_deletable", { name => $self->name });
|
||||
}
|
||||
|
||||
# Groups having a special role cannot be deleted.
|
||||
my @special_groups;
|
||||
foreach my $special_group (GROUP_PARAMS) {
|
||||
if ($self->name eq Bugzilla->params->{$special_group}) {
|
||||
push(@special_groups, $special_group);
|
||||
}
|
||||
}
|
||||
if (scalar(@special_groups)) {
|
||||
ThrowUserError('group_has_special_role',
|
||||
{ name => $self->name,
|
||||
groups => \@special_groups });
|
||||
}
|
||||
|
||||
return if $params->{'test_only'};
|
||||
|
||||
my $cantdelete = 0;
|
||||
|
||||
my $users = $self->members_non_inherited;
|
||||
if (scalar(@$users) && !$params->{'remove_from_users'}) {
|
||||
$cantdelete = 1;
|
||||
}
|
||||
|
||||
my $bugs = $self->bugs;
|
||||
if (scalar(@$bugs) && !$params->{'remove_from_bugs'}) {
|
||||
$cantdelete = 1;
|
||||
}
|
||||
|
||||
my $products = $self->products;
|
||||
if (scalar(@$products) && !$params->{'remove_from_products'}) {
|
||||
$cantdelete = 1;
|
||||
}
|
||||
|
||||
my $flag_types = $self->flag_types;
|
||||
if (scalar(@$flag_types) && !$params->{'remove_from_flags'}) {
|
||||
$cantdelete = 1;
|
||||
}
|
||||
|
||||
ThrowUserError('group_cannot_delete', { group => $self }) if $cantdelete;
|
||||
}
|
||||
|
||||
sub remove_from_db {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$self->check_remove(@_);
|
||||
$dbh->bz_start_transaction();
|
||||
Bugzilla::Hook::process('group_before_delete', { group => $self });
|
||||
$dbh->do('DELETE FROM whine_schedules
|
||||
WHERE mailto_type = ? AND mailto = ?',
|
||||
undef, MAILTO_GROUP, $self->id);
|
||||
# All the other tables will be handled by foreign keys when we
|
||||
# drop the main "groups" row.
|
||||
$self->SUPER::remove_from_db(@_);
|
||||
$dbh->bz_commit_transaction();
|
||||
}
|
||||
|
||||
# Add missing entries in bug_group_map for bugs created while
|
||||
# a mandatory group was disabled and which is now enabled again.
|
||||
sub _enforce_mandatory {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $gid = $self->id;
|
||||
|
||||
my $bug_ids =
|
||||
$dbh->selectcol_arrayref('SELECT bugs.bug_id
|
||||
FROM bugs
|
||||
INNER JOIN group_control_map
|
||||
ON group_control_map.product_id = bugs.product_id
|
||||
LEFT JOIN bug_group_map
|
||||
ON bug_group_map.bug_id = bugs.bug_id
|
||||
AND bug_group_map.group_id = group_control_map.group_id
|
||||
WHERE group_control_map.group_id = ?
|
||||
AND group_control_map.membercontrol = ?
|
||||
AND bug_group_map.group_id IS NULL',
|
||||
undef, ($gid, CONTROLMAPMANDATORY));
|
||||
|
||||
my $sth = $dbh->prepare('INSERT INTO bug_group_map (bug_id, group_id) VALUES (?, ?)');
|
||||
foreach my $bug_id (@$bug_ids) {
|
||||
$sth->execute($bug_id, $gid);
|
||||
}
|
||||
}
|
||||
|
||||
sub is_active_bug_group {
|
||||
my $self = shift;
|
||||
return $self->is_active && $self->is_bug_group;
|
||||
}
|
||||
|
||||
sub _rederive_regexp {
|
||||
my ($self) = @_;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare("SELECT userid, login_name, group_id
|
||||
FROM profiles
|
||||
LEFT JOIN user_group_map
|
||||
ON user_group_map.user_id = profiles.userid
|
||||
AND group_id = ?
|
||||
AND grant_type = ?
|
||||
AND isbless = 0");
|
||||
my $sthadd = $dbh->prepare("INSERT INTO user_group_map
|
||||
(user_id, group_id, grant_type, isbless)
|
||||
VALUES (?, ?, ?, 0)");
|
||||
my $sthdel = $dbh->prepare("DELETE FROM user_group_map
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
AND grant_type = ? and isbless = 0");
|
||||
$sth->execute($self->id, GRANT_REGEXP);
|
||||
my $regexp = $self->user_regexp;
|
||||
while (my ($uid, $login, $present) = $sth->fetchrow_array) {
|
||||
if ($regexp ne '' and $login =~ /$regexp/i) {
|
||||
$sthadd->execute($uid, $self->id, GRANT_REGEXP) unless $present;
|
||||
} else {
|
||||
$sthdel->execute($uid, $self->id, GRANT_REGEXP) if $present;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub flatten_group_membership {
|
||||
my ($self, @groups) = @_;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth;
|
||||
my @groupidstocheck = @groups;
|
||||
my %groupidschecked = ();
|
||||
$sth = $dbh->prepare("SELECT member_id FROM group_group_map
|
||||
WHERE grantor_id = ?
|
||||
AND grant_type = " . GROUP_MEMBERSHIP);
|
||||
while (my $node = shift @groupidstocheck) {
|
||||
$sth->execute($node);
|
||||
my $member;
|
||||
while (($member) = $sth->fetchrow_array) {
|
||||
if (!$groupidschecked{$member}) {
|
||||
$groupidschecked{$member} = 1;
|
||||
push @groupidstocheck, $member;
|
||||
push @groups, $member unless grep $_ == $member, @groups;
|
||||
}
|
||||
}
|
||||
}
|
||||
return \@groups;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
################################
|
||||
##### Module Subroutines ###
|
||||
################################
|
||||
|
||||
sub create {
|
||||
my $class = shift;
|
||||
my ($params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
print get_text('install_group_create', { name => $params->{name} }) . "\n"
|
||||
if Bugzilla->usage_mode == USAGE_MODE_CMDLINE;
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
my $group = $class->SUPER::create(@_);
|
||||
|
||||
# Since we created a new group, give the "admin" group all privileges
|
||||
# initially.
|
||||
my $admin = new Bugzilla::Group({name => 'admin'});
|
||||
# This function is also used to create the "admin" group itself,
|
||||
# so there's a chance it won't exist yet.
|
||||
if ($admin) {
|
||||
my $sth = $dbh->prepare('INSERT INTO group_group_map
|
||||
(member_id, grantor_id, grant_type)
|
||||
VALUES (?, ?, ?)');
|
||||
$sth->execute($admin->id, $group->id, GROUP_MEMBERSHIP);
|
||||
$sth->execute($admin->id, $group->id, GROUP_BLESS);
|
||||
$sth->execute($admin->id, $group->id, GROUP_VISIBLE);
|
||||
}
|
||||
|
||||
$group->_rederive_regexp() if $group->user_regexp;
|
||||
|
||||
Bugzilla::Hook::process('group_end_of_create', { group => $group });
|
||||
$dbh->bz_commit_transaction();
|
||||
return $group;
|
||||
}
|
||||
|
||||
sub ValidateGroupName {
|
||||
my ($name, @users) = (@_);
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $query = "SELECT id FROM groups " .
|
||||
"WHERE name = ?";
|
||||
if (Bugzilla->params->{'usevisibilitygroups'}) {
|
||||
my @visible = (-1);
|
||||
foreach my $user (@users) {
|
||||
$user && push @visible, @{$user->visible_groups_direct};
|
||||
}
|
||||
my $visible = join(', ', @visible);
|
||||
$query .= " AND id IN($visible)";
|
||||
}
|
||||
my $sth = $dbh->prepare($query);
|
||||
$sth->execute($name);
|
||||
my ($ret) = $sth->fetchrow_array();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub check_no_disclose {
|
||||
my ($class, $params) = @_;
|
||||
my $action = delete $params->{action};
|
||||
|
||||
$action =~ /^(?:add|remove)$/
|
||||
or ThrowCodeError('bad_arg', { argument => $action,
|
||||
function => "${class}::check_no_disclose" });
|
||||
|
||||
$params->{_error} = ($action eq 'add') ? 'group_restriction_not_allowed'
|
||||
: 'group_invalid_removal';
|
||||
|
||||
my $group = $class->check($params);
|
||||
return $group;
|
||||
}
|
||||
|
||||
###############################
|
||||
### Validators ###
|
||||
###############################
|
||||
|
||||
sub _check_name {
|
||||
my ($invocant, $name) = @_;
|
||||
$name = trim($name);
|
||||
$name || ThrowUserError("empty_group_name");
|
||||
# If we're creating a Group or changing the name...
|
||||
if (!ref($invocant) || lc($invocant->name) ne lc($name)) {
|
||||
my $exists = new Bugzilla::Group({name => $name });
|
||||
ThrowUserError("group_exists", { name => $name }) if $exists;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub _check_description {
|
||||
my ($invocant, $desc) = @_;
|
||||
$desc = trim($desc);
|
||||
$desc || ThrowUserError("empty_group_description");
|
||||
return $desc;
|
||||
}
|
||||
|
||||
sub _check_user_regexp {
|
||||
my ($invocant, $regex) = @_;
|
||||
$regex = trim($regex) || '';
|
||||
ThrowUserError("invalid_regexp") unless (eval {qr/$regex/});
|
||||
return $regex;
|
||||
}
|
||||
|
||||
sub _check_is_active { return $_[1] ? 1 : 0; }
|
||||
sub _check_is_bug_group {
|
||||
return $_[1] ? 1 : 0;
|
||||
}
|
||||
|
||||
sub _check_icon_url { return $_[1] ? clean_text($_[1]) : undef; }
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Group - Bugzilla group class.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Group;
|
||||
|
||||
my $group = new Bugzilla::Group(1);
|
||||
my $group = new Bugzilla::Group({name => 'AcmeGroup'});
|
||||
|
||||
my $id = $group->id;
|
||||
my $name = $group->name;
|
||||
my $description = $group->description;
|
||||
my $user_reg_exp = $group->user_reg_exp;
|
||||
my $is_active = $group->is_active;
|
||||
my $icon_url = $group->icon_url;
|
||||
my $is_active_bug_group = $group->is_active_bug_group;
|
||||
|
||||
my $group_id = Bugzilla::Group::ValidateGroupName('admin', @users);
|
||||
my @groups = Bugzilla::Group->get_all;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Group.pm represents a Bugzilla Group object. It is an implementation
|
||||
of L<Bugzilla::Object>, and thus has all the methods that L<Bugzilla::Object>
|
||||
provides, in addition to any methods documented below.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=over
|
||||
|
||||
=item C<create>
|
||||
|
||||
Note that in addition to what L<Bugzilla::Object/create($params)>
|
||||
normally does, this function also makes the new group be inherited
|
||||
by the C<admin> group. That is, the C<admin> group will automatically
|
||||
be a member of this group.
|
||||
|
||||
=item C<ValidateGroupName($name, @users)>
|
||||
|
||||
Description: ValidateGroupName checks to see if ANY of the users
|
||||
in the provided list of user objects can see the
|
||||
named group.
|
||||
|
||||
Params: $name - String with the group name.
|
||||
@users - An array with Bugzilla::User objects.
|
||||
|
||||
Returns: It returns the group id if successful
|
||||
and undef otherwise.
|
||||
|
||||
=back
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<check_no_disclose>
|
||||
|
||||
=over
|
||||
|
||||
=item B<Description>
|
||||
|
||||
Throws an error if the user cannot add or remove this group to/from a given
|
||||
bug, but doesn't specify if this is because the group doesn't exist, or the
|
||||
user is not allowed to edit this group restriction.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
This method takes a single hashref as argument, with the following keys:
|
||||
|
||||
=over
|
||||
|
||||
=item C<name>
|
||||
|
||||
C<string> The name of the group to add or remove.
|
||||
|
||||
=item C<bug_id>
|
||||
|
||||
C<integer> The ID of the bug to which the group change applies.
|
||||
|
||||
=item C<product>
|
||||
|
||||
C<string> The name of the product the bug belongs to.
|
||||
|
||||
=item C<action>
|
||||
|
||||
C<string> Must be either C<add> or C<remove>, depending on whether the group
|
||||
must be added or removed from the bug. Any other value will generate an error.
|
||||
|
||||
=back
|
||||
|
||||
=item C<Returns>
|
||||
|
||||
A C<Bugzilla::Group> object on success, else an error is thrown.
|
||||
|
||||
=back
|
||||
|
||||
=item C<check_remove>
|
||||
|
||||
=over
|
||||
|
||||
=item B<Description>
|
||||
|
||||
Determines whether it's OK to remove this group from the database, and
|
||||
throws an error if it's not OK.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
||||
=item C<test_only>
|
||||
|
||||
C<boolean> If you want to only check if the group can be deleted I<at all>,
|
||||
under any circumstances, specify C<test_only> to just do the most basic tests
|
||||
(the other parameters will be ignored in this situation, as those tests won't
|
||||
be run).
|
||||
|
||||
=item C<remove_from_users>
|
||||
|
||||
C<boolean> True if it would be OK to remove all users who are in this group
|
||||
from this group.
|
||||
|
||||
=item C<remove_from_bugs>
|
||||
|
||||
C<boolean> True if it would be OK to remove all bugs that are in this group
|
||||
from this group.
|
||||
|
||||
=item C<remove_from_flags>
|
||||
|
||||
C<boolean> True if it would be OK to stop all flagtypes that reference
|
||||
this group from referencing this group (e.g., as their grantgroup or
|
||||
requestgroup).
|
||||
|
||||
=item C<remove_from_products>
|
||||
|
||||
C<boolean> True if it would be OK to remove this group from all group controls
|
||||
on products.
|
||||
|
||||
=back
|
||||
|
||||
=item B<Returns> (nothing)
|
||||
|
||||
=back
|
||||
|
||||
=item C<members_non_inherited>
|
||||
|
||||
Returns an arrayref of L<Bugzilla::User> objects representing people who are
|
||||
"directly" in this group, meaning that they're in it because they match
|
||||
the group regular expression, or they have been actually added to the
|
||||
group manually.
|
||||
|
||||
=item C<flatten_group_membership>
|
||||
|
||||
Accepts a list of groups and returns a list of all the groups whose members
|
||||
inherit membership in any group on the list. So, we can determine if a user
|
||||
is in any of the groups input to flatten_group_membership by querying the
|
||||
user_group_map for any user with DIRECT or REGEXP membership IN() the list
|
||||
of groups returned.
|
||||
|
||||
=back
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,468 +0,0 @@
|
||||
# -*- 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): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Install;
|
||||
|
||||
# Functions in this this package can assume that the database
|
||||
# has been set up, params are available, localconfig is
|
||||
# available, and any module can be used.
|
||||
#
|
||||
# If you want to write an installation function that can't
|
||||
# make those assumptions, then it should go into one of the
|
||||
# packages under the Bugzilla::Install namespace.
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Component;
|
||||
use Bugzilla::Config qw(:admin);
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Group;
|
||||
use Bugzilla::Product;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::User::Setting;
|
||||
use Bugzilla::Util qw(get_text);
|
||||
use Bugzilla::Version;
|
||||
|
||||
use constant STATUS_WORKFLOW => (
|
||||
[undef, 'UNCONFIRMED'],
|
||||
[undef, 'CONFIRMED'],
|
||||
[undef, 'IN_PROGRESS'],
|
||||
['UNCONFIRMED', 'CONFIRMED'],
|
||||
['UNCONFIRMED', 'IN_PROGRESS'],
|
||||
['UNCONFIRMED', 'RESOLVED'],
|
||||
['CONFIRMED', 'IN_PROGRESS'],
|
||||
['CONFIRMED', 'RESOLVED'],
|
||||
['IN_PROGRESS', 'CONFIRMED'],
|
||||
['IN_PROGRESS', 'RESOLVED'],
|
||||
['RESOLVED', 'UNCONFIRMED'],
|
||||
['RESOLVED', 'CONFIRMED'],
|
||||
['RESOLVED', 'VERIFIED'],
|
||||
['VERIFIED', 'UNCONFIRMED'],
|
||||
['VERIFIED', 'CONFIRMED'],
|
||||
);
|
||||
|
||||
sub SETTINGS {
|
||||
return {
|
||||
# 2005-03-03 travis@sedsystems.ca -- Bug 41972
|
||||
display_quips => { options => ["on", "off"], default => "on" },
|
||||
# 2005-03-10 travis@sedsystems.ca -- Bug 199048
|
||||
comment_sort_order => { options => ["oldest_to_newest", "newest_to_oldest",
|
||||
"newest_to_oldest_desc_first"],
|
||||
default => "oldest_to_newest" },
|
||||
# 2005-05-12 bugzilla@glob.com.au -- Bug 63536
|
||||
post_bug_submit_action => { options => ["next_bug", "same_bug", "nothing"],
|
||||
default => "next_bug" },
|
||||
# 2005-06-29 wurblzap@gmail.com -- Bug 257767
|
||||
csv_colsepchar => { options => [',',';'], default => ',' },
|
||||
# 2005-10-26 wurblzap@gmail.com -- Bug 291459
|
||||
zoom_textareas => { options => ["on", "off"], default => "on" },
|
||||
# 2005-10-21 LpSolit@gmail.com -- Bug 313020
|
||||
per_bug_queries => { options => ['on', 'off'], default => 'off' },
|
||||
# 2006-05-01 olav@bkor.dhs.org -- Bug 7710
|
||||
state_addselfcc => { options => ['always', 'never', 'cc_unless_role'],
|
||||
default => 'cc_unless_role' },
|
||||
# 2006-08-04 wurblzap@gmail.com -- Bug 322693
|
||||
skin => { subclass => 'Skin', default => 'Dusk' },
|
||||
# 2006-12-10 LpSolit@gmail.com -- Bug 297186
|
||||
lang => { subclass => 'Lang',
|
||||
default => ${Bugzilla->languages}[0] },
|
||||
# 2007-07-02 altlist@gmail.com -- Bug 225731
|
||||
quote_replies => { options => ['quoted_reply', 'simple_reply', 'off'],
|
||||
default => "quoted_reply" },
|
||||
# 2009-02-01 mozilla@matt.mchenryfamily.org -- Bug 398473
|
||||
comment_box_position => { options => ['before_comments', 'after_comments'],
|
||||
default => 'before_comments' },
|
||||
# 2008-08-27 LpSolit@gmail.com -- Bug 182238
|
||||
timezone => { subclass => 'Timezone', default => 'local' },
|
||||
}
|
||||
};
|
||||
|
||||
use constant SYSTEM_GROUPS => (
|
||||
{
|
||||
name => 'admin',
|
||||
description => 'Administrators'
|
||||
},
|
||||
{
|
||||
name => 'tweakparams',
|
||||
description => 'Can change Parameters'
|
||||
},
|
||||
{
|
||||
name => 'editusers',
|
||||
description => 'Can edit or disable users'
|
||||
},
|
||||
{
|
||||
name => 'creategroups',
|
||||
description => 'Can create and destroy groups'
|
||||
},
|
||||
{
|
||||
name => 'editclassifications',
|
||||
description => 'Can create, destroy, and edit classifications'
|
||||
},
|
||||
{
|
||||
name => 'editcomponents',
|
||||
description => 'Can create, destroy, and edit components'
|
||||
},
|
||||
{
|
||||
name => 'editkeywords',
|
||||
description => 'Can create, destroy, and edit keywords'
|
||||
},
|
||||
{
|
||||
name => 'editbugs',
|
||||
description => 'Can edit all bug fields',
|
||||
userregexp => '.*'
|
||||
},
|
||||
{
|
||||
name => 'canconfirm',
|
||||
description => 'Can confirm a bug or mark it a duplicate'
|
||||
},
|
||||
{
|
||||
name => 'bz_canusewhineatothers',
|
||||
description => 'Can configure whine reports for other users',
|
||||
},
|
||||
{
|
||||
name => 'bz_canusewhines',
|
||||
description => 'User can configure whine reports for self',
|
||||
# inherited_by means that users in the groups listed below are
|
||||
# automatically members of bz_canusewhines.
|
||||
inherited_by => ['editbugs', 'bz_canusewhineatothers'],
|
||||
},
|
||||
{
|
||||
name => 'bz_sudoers',
|
||||
description => 'Can perform actions as other users',
|
||||
},
|
||||
{
|
||||
name => 'bz_sudo_protect',
|
||||
description => 'Can not be impersonated by other users',
|
||||
inherited_by => ['bz_sudoers'],
|
||||
},
|
||||
);
|
||||
|
||||
use constant DEFAULT_CLASSIFICATION => {
|
||||
name => 'Unclassified',
|
||||
description => 'Not assigned to any classification'
|
||||
};
|
||||
|
||||
use constant DEFAULT_PRODUCT => {
|
||||
name => 'TestProduct',
|
||||
description => 'This is a test product.'
|
||||
. ' This ought to be blown away and replaced with real stuff in a'
|
||||
. ' finished installation of bugzilla.',
|
||||
version => Bugzilla::Version::DEFAULT_VERSION,
|
||||
classification => 'Unclassified',
|
||||
defaultmilestone => DEFAULT_MILESTONE,
|
||||
};
|
||||
|
||||
use constant DEFAULT_COMPONENT => {
|
||||
name => 'TestComponent',
|
||||
description => 'This is a test component in the test product database.'
|
||||
. ' This ought to be blown away and replaced with real stuff in'
|
||||
. ' a finished installation of Bugzilla.'
|
||||
};
|
||||
|
||||
sub update_settings {
|
||||
my %settings = %{SETTINGS()};
|
||||
foreach my $setting (keys %settings) {
|
||||
add_setting($setting,
|
||||
$settings{$setting}->{options},
|
||||
$settings{$setting}->{default},
|
||||
$settings{$setting}->{subclass});
|
||||
}
|
||||
}
|
||||
|
||||
sub update_system_groups {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
# Create most of the system groups
|
||||
foreach my $definition (SYSTEM_GROUPS) {
|
||||
my $exists = new Bugzilla::Group({ name => $definition->{name} });
|
||||
if (!$exists) {
|
||||
$definition->{isbuggroup} = 0;
|
||||
my $inherited_by = delete $definition->{inherited_by};
|
||||
my $created = Bugzilla::Group->create($definition);
|
||||
# Each group in inherited_by is automatically a member of this
|
||||
# group.
|
||||
if ($inherited_by) {
|
||||
foreach my $name (@$inherited_by) {
|
||||
my $member = Bugzilla::Group->check($name);
|
||||
$dbh->do('INSERT INTO group_group_map (grantor_id,
|
||||
member_id) VALUES (?,?)',
|
||||
undef, $created->id, $member->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
}
|
||||
|
||||
sub create_default_classification {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# Make the default Classification if it doesn't already exist.
|
||||
if (!$dbh->selectrow_array('SELECT 1 FROM classifications')) {
|
||||
print get_text('install_default_classification',
|
||||
{ name => DEFAULT_CLASSIFICATION->{name} }) . "\n";
|
||||
Bugzilla::Classification->create(DEFAULT_CLASSIFICATION);
|
||||
}
|
||||
}
|
||||
|
||||
# This function should be called only after creating the admin user.
|
||||
sub create_default_product {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# And same for the default product/component.
|
||||
if (!$dbh->selectrow_array('SELECT 1 FROM products')) {
|
||||
print get_text('install_default_product',
|
||||
{ name => DEFAULT_PRODUCT->{name} }) . "\n";
|
||||
|
||||
my $product = Bugzilla::Product->create(DEFAULT_PRODUCT);
|
||||
|
||||
# Get the user who will be the owner of the Component.
|
||||
# We pick the admin with the lowest id, which is probably the
|
||||
# admin checksetup.pl just created.
|
||||
my $admin_group = new Bugzilla::Group({name => 'admin'});
|
||||
my ($admin_id) = $dbh->selectrow_array(
|
||||
'SELECT user_id FROM user_group_map WHERE group_id = ?
|
||||
ORDER BY user_id ' . $dbh->sql_limit(1),
|
||||
undef, $admin_group->id);
|
||||
my $admin = Bugzilla::User->new($admin_id);
|
||||
|
||||
Bugzilla::Component->create({
|
||||
%{ DEFAULT_COMPONENT() }, product => $product,
|
||||
initialowner => $admin->login });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub init_workflow {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $has_workflow = $dbh->selectrow_array('SELECT 1 FROM status_workflow');
|
||||
return if $has_workflow;
|
||||
|
||||
print get_text('install_workflow_init'), "\n";
|
||||
|
||||
my %status_ids = @{ $dbh->selectcol_arrayref(
|
||||
'SELECT value, id FROM bug_status', {Columns=>[1,2]}) };
|
||||
|
||||
foreach my $pair (STATUS_WORKFLOW) {
|
||||
my $old_id = $pair->[0] ? $status_ids{$pair->[0]} : undef;
|
||||
my $new_id = $status_ids{$pair->[1]};
|
||||
$dbh->do('INSERT INTO status_workflow (old_status, new_status)
|
||||
VALUES (?,?)', undef, $old_id, $new_id);
|
||||
}
|
||||
}
|
||||
|
||||
sub create_admin {
|
||||
my ($params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $template = Bugzilla->template;
|
||||
|
||||
my $admin_group = new Bugzilla::Group({ name => 'admin' });
|
||||
my $admin_inheritors =
|
||||
Bugzilla::Group->flatten_group_membership($admin_group->id);
|
||||
my $admin_group_ids = join(',', @$admin_inheritors);
|
||||
|
||||
my ($admin_count) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*) FROM user_group_map
|
||||
WHERE group_id IN ($admin_group_ids)");
|
||||
|
||||
return if $admin_count;
|
||||
|
||||
my %answer = %{Bugzilla->installation_answers};
|
||||
my $login = $answer{'ADMIN_EMAIL'};
|
||||
my $password = $answer{'ADMIN_PASSWORD'};
|
||||
my $full_name = $answer{'ADMIN_REALNAME'};
|
||||
|
||||
if (!$login || !$password || !$full_name) {
|
||||
print "\n" . get_text('install_admin_setup') . "\n\n";
|
||||
}
|
||||
|
||||
while (!$login) {
|
||||
print get_text('install_admin_get_email') . ' ';
|
||||
$login = <STDIN>;
|
||||
chomp $login;
|
||||
eval { Bugzilla::User->check_login_name_for_creation($login); };
|
||||
if ($@) {
|
||||
print $@ . "\n";
|
||||
undef $login;
|
||||
}
|
||||
}
|
||||
|
||||
while (!defined $full_name) {
|
||||
print get_text('install_admin_get_name') . ' ';
|
||||
$full_name = <STDIN>;
|
||||
chomp($full_name);
|
||||
}
|
||||
|
||||
if (!$password) {
|
||||
$password = _prompt_for_password(
|
||||
get_text('install_admin_get_password'));
|
||||
}
|
||||
|
||||
my $admin = Bugzilla::User->create({ login_name => $login,
|
||||
realname => $full_name,
|
||||
cryptpassword => $password });
|
||||
make_admin($admin);
|
||||
}
|
||||
|
||||
sub make_admin {
|
||||
my ($user) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$user = ref($user) ? $user
|
||||
: new Bugzilla::User(login_to_id($user, THROW_ERROR));
|
||||
|
||||
my $admin_group = new Bugzilla::Group({ name => 'admin' });
|
||||
|
||||
# Admins get explicit membership and bless capability for the admin group
|
||||
$dbh->selectrow_array("SELECT id FROM groups WHERE name = 'admin'");
|
||||
|
||||
my $group_insert = $dbh->prepare(
|
||||
'INSERT INTO user_group_map (user_id, group_id, isbless, grant_type)
|
||||
VALUES (?, ?, ?, ?)');
|
||||
# These are run in an eval so that we can ignore the error of somebody
|
||||
# already being granted these things.
|
||||
eval {
|
||||
$group_insert->execute($user->id, $admin_group->id, 0, GRANT_DIRECT);
|
||||
};
|
||||
eval {
|
||||
$group_insert->execute($user->id, $admin_group->id, 1, GRANT_DIRECT);
|
||||
};
|
||||
|
||||
# Admins should also have editusers directly, even though they'll usually
|
||||
# inherit it. People could have changed their inheritance structure.
|
||||
my $editusers = new Bugzilla::Group({ name => 'editusers' });
|
||||
eval {
|
||||
$group_insert->execute($user->id, $editusers->id, 0, GRANT_DIRECT);
|
||||
};
|
||||
|
||||
# If there is no maintainer set, make this user the maintainer.
|
||||
if (!Bugzilla->params->{'maintainer'}) {
|
||||
SetParam('maintainer', $user->email);
|
||||
write_params();
|
||||
}
|
||||
|
||||
if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
|
||||
print "\n", get_text('install_admin_created', { user => $user }), "\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub _prompt_for_password {
|
||||
my $prompt = shift;
|
||||
|
||||
my $password;
|
||||
while (!$password) {
|
||||
# trap a few interrupts so we can fix the echo if we get aborted.
|
||||
local $SIG{HUP} = \&_password_prompt_exit;
|
||||
local $SIG{INT} = \&_password_prompt_exit;
|
||||
local $SIG{QUIT} = \&_password_prompt_exit;
|
||||
local $SIG{TERM} = \&_password_prompt_exit;
|
||||
|
||||
system("stty","-echo") unless ON_WINDOWS; # disable input echoing
|
||||
|
||||
print $prompt, ' ';
|
||||
$password = <STDIN>;
|
||||
chomp $password;
|
||||
print "\n", get_text('install_confirm_password'), ' ';
|
||||
my $pass2 = <STDIN>;
|
||||
chomp $pass2;
|
||||
eval { validate_password($password, $pass2); };
|
||||
if ($@) {
|
||||
print "\n$@\n";
|
||||
undef $password;
|
||||
}
|
||||
system("stty","echo") unless ON_WINDOWS;
|
||||
}
|
||||
return $password;
|
||||
}
|
||||
|
||||
# This is just in case we get interrupted while getting a password.
|
||||
sub _password_prompt_exit {
|
||||
# re-enable input echoing
|
||||
system("stty","echo") unless ON_WINDOWS;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub reset_password {
|
||||
my $login = shift;
|
||||
my $user = Bugzilla::User->check($login);
|
||||
my $prompt = "\n" . get_text('install_reset_password', { user => $user });
|
||||
my $password = _prompt_for_password($prompt);
|
||||
$user->set_password($password);
|
||||
$user->update();
|
||||
print "\n", get_text('install_reset_password_done'), "\n";
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Install - Functions and variables having to do with
|
||||
installation.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Install;
|
||||
Bugzilla::Install::update_settings();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is used primarily by L<checksetup.pl> during installation.
|
||||
This module contains functions that deal with general installation
|
||||
issues after the database is completely set up and configured.
|
||||
|
||||
=head1 CONSTANTS
|
||||
|
||||
=over
|
||||
|
||||
=item C<SETTINGS>
|
||||
|
||||
Contains information about Settings, used by L</update_settings()>.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=over
|
||||
|
||||
=item C<update_settings()>
|
||||
|
||||
Description: Adds and updates Settings for users.
|
||||
|
||||
Params: none
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<create_default_classification>
|
||||
|
||||
Creates the default "Unclassified" L<Classification|Bugzilla::Classification>
|
||||
if it doesn't already exist
|
||||
|
||||
=item C<create_default_product()>
|
||||
|
||||
Description: Creates the default product and component if
|
||||
they don't exist.
|
||||
|
||||
Params: none
|
||||
|
||||
Returns: nothing
|
||||
|
||||
=back
|
||||
@@ -1,349 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Everything Solved, Inc.
|
||||
# Portions created by Everything Solved are Copyright (C) 2007
|
||||
# Everything Solved, Inc. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Install::CPAN;
|
||||
use strict;
|
||||
use base qw(Exporter);
|
||||
our @EXPORT = qw(
|
||||
BZ_LIB
|
||||
|
||||
check_cpan_requirements
|
||||
set_cpan_config
|
||||
install_module
|
||||
);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Install::Requirements qw(have_vers);
|
||||
use Bugzilla::Install::Util qw(bin_loc install_string);
|
||||
|
||||
use CPAN;
|
||||
use Cwd qw(abs_path);
|
||||
use File::Path qw(rmtree);
|
||||
use List::Util qw(shuffle);
|
||||
|
||||
# These are required for install-module.pl to be able to install
|
||||
# all modules properly.
|
||||
use constant REQUIREMENTS => (
|
||||
{
|
||||
module => 'CPAN',
|
||||
package => 'CPAN',
|
||||
version => '1.81',
|
||||
},
|
||||
{
|
||||
# When Module::Build isn't installed, the YAML module allows
|
||||
# CPAN to read META.yml to determine that Module::Build first
|
||||
# needs to be installed to compile a module.
|
||||
module => 'YAML',
|
||||
package => 'YAML',
|
||||
version => 0,
|
||||
},
|
||||
{
|
||||
# Many modules on CPAN are now built with Dist::Zilla, which
|
||||
# unfortunately means they require this version of EU::MM to install.
|
||||
module => 'ExtUtils::MakeMaker',
|
||||
package => 'ExtUtils-MakeMaker',
|
||||
version => '6.31',
|
||||
},
|
||||
);
|
||||
|
||||
# We need the absolute path of ext_libpath, because CPAN chdirs around
|
||||
# and so we can't use a relative directory.
|
||||
#
|
||||
# We need it often enough (and at compile time, in install-module.pl) so
|
||||
# we make it a constant.
|
||||
use constant BZ_LIB => abs_path(bz_locations()->{ext_libpath});
|
||||
|
||||
# These modules are problematic to install with "notest" (sometimes they
|
||||
# get installed when they shouldn't). So we always test their installation
|
||||
# and never ignore test failures.
|
||||
use constant ALWAYS_TEST => qw(
|
||||
Math::Random::Secure
|
||||
);
|
||||
|
||||
# CPAN requires nearly all of its parameters to be set, or it will start
|
||||
# asking questions to the user. We want to avoid that, so we have
|
||||
# defaults here for most of the required parameters we know about, in case
|
||||
# any of them aren't set. The rest are handled by set_cpan_defaults().
|
||||
use constant CPAN_DEFAULTS => {
|
||||
auto_commit => 0,
|
||||
# We always force builds, so there's no reason to cache them.
|
||||
build_cache => 0,
|
||||
build_requires_install_policy => 'yes',
|
||||
cache_metadata => 1,
|
||||
colorize_output => 1,
|
||||
colorize_print => 'bold',
|
||||
index_expire => 1,
|
||||
scan_cache => 'atstart',
|
||||
|
||||
inhibit_startup_message => 1,
|
||||
|
||||
bzip2 => bin_loc('bzip2'),
|
||||
curl => bin_loc('curl'),
|
||||
gzip => bin_loc('gzip'),
|
||||
links => bin_loc('links'),
|
||||
lynx => bin_loc('lynx'),
|
||||
make => bin_loc('make'),
|
||||
pager => bin_loc('less'),
|
||||
tar => bin_loc('tar'),
|
||||
unzip => bin_loc('unzip'),
|
||||
wget => bin_loc('wget'),
|
||||
|
||||
urllist => [shuffle qw(
|
||||
http://cpan.pair.com/
|
||||
http://mirror.hiwaay.net/CPAN/
|
||||
ftp://ftp.dc.aleron.net/pub/CPAN/
|
||||
http://mirrors.kernel.org/cpan/
|
||||
http://mirrors2.kernel.org/cpan/)],
|
||||
};
|
||||
|
||||
sub check_cpan_requirements {
|
||||
my ($original_dir, $original_args) = @_;
|
||||
|
||||
my @install;
|
||||
foreach my $module (REQUIREMENTS) {
|
||||
my $installed = have_vers($module, 1);
|
||||
push(@install, $module) if !$installed;
|
||||
}
|
||||
|
||||
return if !@install;
|
||||
|
||||
my $restart_required;
|
||||
foreach my $module (@install) {
|
||||
$restart_required = 1 if $module->{module} eq 'CPAN';
|
||||
install_module($module->{module}, 1);
|
||||
}
|
||||
|
||||
if ($restart_required) {
|
||||
chdir $original_dir;
|
||||
exec($^X, $0, @$original_args);
|
||||
}
|
||||
}
|
||||
|
||||
sub install_module {
|
||||
my ($name, $test) = @_;
|
||||
my $bzlib = BZ_LIB;
|
||||
|
||||
# Make Module::AutoInstall install all dependencies and never prompt.
|
||||
local $ENV{PERL_AUTOINSTALL} = '--alldeps';
|
||||
# This makes Net::SSLeay not prompt the user, if it gets installed.
|
||||
# It also makes any other MakeMaker prompts accept their defaults.
|
||||
local $ENV{PERL_MM_USE_DEFAULT} = 1;
|
||||
|
||||
# Certain modules require special stuff in order to not prompt us.
|
||||
my $original_makepl = $CPAN::Config->{makepl_arg};
|
||||
# This one's a regex in case we're doing Template::Plugin::GD and it
|
||||
# pulls in Template-Toolkit as a dependency.
|
||||
if ($name =~ /^Template/) {
|
||||
$CPAN::Config->{makepl_arg} .= " TT_ACCEPT=y TT_EXTRAS=n";
|
||||
}
|
||||
elsif ($name eq 'XML::Twig') {
|
||||
$CPAN::Config->{makepl_arg} = "-n $original_makepl";
|
||||
}
|
||||
elsif ($name eq 'SOAP::Lite') {
|
||||
$CPAN::Config->{makepl_arg} .= " --noprompt";
|
||||
}
|
||||
|
||||
my $module = CPAN::Shell->expand('Module', $name);
|
||||
if (!$module) {
|
||||
die install_string('no_such_module', { module => $name }) . "\n";
|
||||
}
|
||||
my $version = $module->cpan_version;
|
||||
my $module_name = $name;
|
||||
|
||||
if ($name eq 'LWP::UserAgent' && $^V lt v5.8.8) {
|
||||
# LWP 6.x requires Perl 5.8.8 or newer.
|
||||
# As PAUSE only indexes the very last version of each module,
|
||||
# we have to specify the path to the tarball ourselves.
|
||||
$name = 'GAAS/libwww-perl-5.837.tar.gz';
|
||||
# This tarball contains LWP::UserAgent 5.835.
|
||||
$version = '5.835';
|
||||
}
|
||||
|
||||
print install_string('install_module',
|
||||
{ module => $module_name, version => $version }) . "\n";
|
||||
|
||||
if (_always_test($name)) {
|
||||
CPAN::Shell->install($name);
|
||||
}
|
||||
elsif ($test) {
|
||||
CPAN::Shell->force('install', $name);
|
||||
}
|
||||
else {
|
||||
CPAN::Shell->notest('install', $name);
|
||||
}
|
||||
|
||||
# If it installed any binaries in the Bugzilla directory, delete them.
|
||||
if (-d "$bzlib/bin") {
|
||||
File::Path::rmtree("$bzlib/bin");
|
||||
}
|
||||
|
||||
$CPAN::Config->{makepl_arg} = $original_makepl;
|
||||
}
|
||||
|
||||
sub _always_test {
|
||||
my ($name) = @_;
|
||||
return grep(lc($_) eq lc($name), ALWAYS_TEST) ? 1 : 0;
|
||||
}
|
||||
|
||||
sub set_cpan_config {
|
||||
my $do_global = shift;
|
||||
my $bzlib = BZ_LIB;
|
||||
|
||||
# We set defaults before we do anything, otherwise CPAN will
|
||||
# start asking us questions as soon as we load its configuration.
|
||||
eval { require CPAN::Config; };
|
||||
_set_cpan_defaults();
|
||||
|
||||
# Calling a senseless autoload that does nothing makes us
|
||||
# automatically load any existing configuration.
|
||||
# We want to avoid the "invalid command" message.
|
||||
open(my $saveout, ">&", "STDOUT");
|
||||
open(STDOUT, '>', '/dev/null');
|
||||
eval { CPAN->ignore_this_error_message_from_bugzilla; };
|
||||
undef $@;
|
||||
close(STDOUT);
|
||||
open(STDOUT, '>&', $saveout);
|
||||
|
||||
my $dir = $CPAN::Config->{cpan_home};
|
||||
if (!defined $dir || !-w $dir) {
|
||||
# If we can't use the standard CPAN build dir, we try to make one.
|
||||
$dir = "$ENV{HOME}/.cpan";
|
||||
mkdir $dir;
|
||||
|
||||
# If we can't make one, we finally try to use the Bugzilla directory.
|
||||
if (!-w $dir) {
|
||||
print "WARNING: Using the Bugzilla directory as the CPAN home.\n";
|
||||
$dir = "$bzlib/.cpan";
|
||||
}
|
||||
}
|
||||
$CPAN::Config->{cpan_home} = $dir;
|
||||
$CPAN::Config->{build_dir} = "$dir/build";
|
||||
# We always force builds, so there's no reason to cache them.
|
||||
$CPAN::Config->{keep_source_where} = "$dir/source";
|
||||
# This is set both here and in defaults so that it's always true.
|
||||
$CPAN::Config->{inhibit_startup_message} = 1;
|
||||
# Automatically install dependencies.
|
||||
$CPAN::Config->{prerequisites_policy} = 'follow';
|
||||
|
||||
# Unless specified, we install the modules into the Bugzilla directory.
|
||||
if (!$do_global) {
|
||||
require Config;
|
||||
|
||||
$CPAN::Config->{makepl_arg} .= " LIB=\"$bzlib\""
|
||||
. " INSTALLMAN1DIR=\"$bzlib/man/man1\""
|
||||
. " INSTALLMAN3DIR=\"$bzlib/man/man3\""
|
||||
# The bindirs are here because otherwise we'll try to write to
|
||||
# the system binary dirs, and that will cause CPAN to die.
|
||||
. " INSTALLBIN=\"$bzlib/bin\""
|
||||
. " INSTALLSCRIPT=\"$bzlib/bin\""
|
||||
# INSTALLDIRS=perl is set because that makes sure that MakeMaker
|
||||
# always uses the directories we've specified here.
|
||||
. " INSTALLDIRS=perl";
|
||||
$CPAN::Config->{mbuild_arg} = " --install_base \"$bzlib\""
|
||||
. " --install_path lib=\"$bzlib\""
|
||||
. " --install_path arch=\"$bzlib/$Config::Config{archname}\"";
|
||||
$CPAN::Config->{mbuild_install_arg} = $CPAN::Config->{mbuild_arg};
|
||||
|
||||
# When we're not root, sometimes newer versions of CPAN will
|
||||
# try to read/modify things that belong to root, unless we set
|
||||
# certain config variables.
|
||||
$CPAN::Config->{histfile} = "$dir/histfile";
|
||||
$CPAN::Config->{use_sqlite} = 0;
|
||||
$CPAN::Config->{prefs_dir} = "$dir/prefs";
|
||||
|
||||
# Unless we actually set PERL5LIB, some modules can't install
|
||||
# themselves, like DBD::mysql, DBD::Pg, and XML::Twig.
|
||||
my $current_lib = $ENV{PERL5LIB} ? $ENV{PERL5LIB} . ':' : '';
|
||||
$ENV{PERL5LIB} = $current_lib . $bzlib;
|
||||
}
|
||||
}
|
||||
|
||||
sub _set_cpan_defaults {
|
||||
# If CPAN hasn't been configured, we try to use some reasonable defaults.
|
||||
foreach my $key (keys %{CPAN_DEFAULTS()}) {
|
||||
$CPAN::Config->{$key} = CPAN_DEFAULTS->{$key}
|
||||
if !defined $CPAN::Config->{$key};
|
||||
}
|
||||
|
||||
my @missing;
|
||||
# In newer CPANs, this is in HandleConfig. In older CPANs, it's in
|
||||
# Config.
|
||||
if (eval { require CPAN::HandleConfig }) {
|
||||
@missing = CPAN::HandleConfig->missing_config_data;
|
||||
}
|
||||
else {
|
||||
@missing = CPAN::Config->missing_config_data;
|
||||
}
|
||||
|
||||
foreach my $key (@missing) {
|
||||
$CPAN::Config->{$key} = '';
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Install::CPAN - Routines to install Perl modules from CPAN.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Install::CPAN;
|
||||
|
||||
set_cpan_config();
|
||||
install_module('Module::Name');
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is primarily used by L<install-module> to do the "hard work" of
|
||||
installing CPAN modules.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=over
|
||||
|
||||
=item C<set_cpan_config>
|
||||
|
||||
Sets up the configuration of CPAN for this session. Must be called
|
||||
before L</install_module>. Takes one boolean parameter. If true,
|
||||
L</install_module> will install modules globally instead of to the
|
||||
local F<lib/> directory. On most systems, you have to be root to do that.
|
||||
|
||||
=item C<install_module>
|
||||
|
||||
Installs a module from CPAN. Takes two arguments:
|
||||
|
||||
=over
|
||||
|
||||
=item C<$name> - The name of the module, just like you'd pass to the
|
||||
C<install> command in the CPAN shell.
|
||||
|
||||
=item C<$test> - If true, we run tests on this module before installing,
|
||||
but we still force the install if the tests fail. This is only used
|
||||
when we internally install a newer CPAN module.
|
||||
|
||||
=back
|
||||
|
||||
Note that calling this function prints a B<lot> of information to
|
||||
STDOUT and STDERR.
|
||||
|
||||
=back
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,848 +0,0 @@
|
||||
# -*- 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): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
# Bill Barry <after.fallout@gmail.com>
|
||||
|
||||
package Bugzilla::Install::Filesystem;
|
||||
|
||||
# NOTE: This package may "use" any modules that it likes,
|
||||
# and localconfig is available. However, all functions in this
|
||||
# package should assume that:
|
||||
#
|
||||
# * Templates are not available.
|
||||
# * Files do not have the correct permissions.
|
||||
# * The database does not exist.
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Install::Localconfig;
|
||||
use Bugzilla::Install::Util qw(install_string);
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Hook;
|
||||
|
||||
use File::Find;
|
||||
use File::Path;
|
||||
use File::Basename;
|
||||
use File::Copy qw(move);
|
||||
use IO::File;
|
||||
use POSIX ();
|
||||
|
||||
use base qw(Exporter);
|
||||
our @EXPORT = qw(
|
||||
update_filesystem
|
||||
create_htaccess
|
||||
fix_all_file_permissions
|
||||
fix_file_permissions
|
||||
);
|
||||
|
||||
use constant HT_DEFAULT_DENY => <<EOT;
|
||||
# nothing in this directory is retrievable unless overridden by an .htaccess
|
||||
# in a subdirectory
|
||||
deny from all
|
||||
EOT
|
||||
|
||||
###############
|
||||
# Permissions #
|
||||
###############
|
||||
|
||||
# Used by the permissions "constants" below.
|
||||
sub _suexec { Bugzilla->localconfig->{'use_suexec'} };
|
||||
sub _group { Bugzilla->localconfig->{'webservergroup'} };
|
||||
|
||||
# Writeable by the owner only.
|
||||
use constant OWNER_WRITE => 0600;
|
||||
# Executable by the owner only.
|
||||
use constant OWNER_EXECUTE => 0700;
|
||||
# A directory which is only writeable by the owner.
|
||||
use constant DIR_OWNER_WRITE => 0700;
|
||||
|
||||
# A cgi script that the webserver can execute.
|
||||
sub WS_EXECUTE { _group() ? 0750 : 0755 };
|
||||
# A file that is read by cgi scripts, but is not ever read
|
||||
# directly by the webserver.
|
||||
sub CGI_READ { _group() ? 0640 : 0644 };
|
||||
# A file that is written to by cgi scripts, but is not ever
|
||||
# read or written directly by the webserver.
|
||||
sub CGI_WRITE { _group() ? 0660 : 0666 };
|
||||
# A file that is served directly by the web server.
|
||||
sub WS_SERVE { (_group() and !_suexec()) ? 0640 : 0644 };
|
||||
|
||||
# A directory whose contents can be read or served by the
|
||||
# webserver (so even directories containing cgi scripts
|
||||
# would have this permission).
|
||||
sub DIR_WS_SERVE { (_group() and !_suexec()) ? 0750 : 0755 };
|
||||
# A directory that is read by cgi scripts, but is never accessed
|
||||
# directly by the webserver
|
||||
sub DIR_CGI_READ { _group() ? 0750 : 0755 };
|
||||
# A directory that is written to by cgi scripts, but where the
|
||||
# scripts never needs to overwrite files created by other
|
||||
# users.
|
||||
sub DIR_CGI_WRITE { _group() ? 0770 : 01777 };
|
||||
# A directory that is written to by cgi scripts, where the
|
||||
# scripts need to overwrite files created by other users.
|
||||
sub DIR_CGI_OVERWRITE { _group() ? 0770 : 0777 };
|
||||
|
||||
# This can be combined (using "|") with other permissions for
|
||||
# directories that, in addition to their normal permissions (such
|
||||
# as DIR_CGI_WRITE) also have content served directly from them
|
||||
# (or their subdirectories) to the user, via the webserver.
|
||||
sub DIR_ALSO_WS_SERVE { _suexec() ? 0001 : 0 };
|
||||
|
||||
# This looks like a constant because it effectively is, but
|
||||
# it has to call other subroutines and read the current filesystem,
|
||||
# so it's defined as a sub. This is not exported, so it doesn't have
|
||||
# a perldoc. However, look at the various hashes defined inside this
|
||||
# function to understand what it returns. (There are comments throughout.)
|
||||
#
|
||||
# The rationale for the file permissions is that there is a group the
|
||||
# web server executes the scripts as, so the cgi scripts should not be writable
|
||||
# by this group. Otherwise someone may find it possible to change the cgis
|
||||
# when exploiting some security flaw somewhere (not necessarily in Bugzilla!)
|
||||
sub FILESYSTEM {
|
||||
my $datadir = bz_locations()->{'datadir'};
|
||||
my $attachdir = bz_locations()->{'attachdir'};
|
||||
my $extensionsdir = bz_locations()->{'extensionsdir'};
|
||||
my $webdotdir = bz_locations()->{'webdotdir'};
|
||||
my $templatedir = bz_locations()->{'templatedir'};
|
||||
my $libdir = bz_locations()->{'libpath'};
|
||||
my $extlib = bz_locations()->{'ext_libpath'};
|
||||
my $skinsdir = bz_locations()->{'skinsdir'};
|
||||
my $localconfig = bz_locations()->{'localconfig'};
|
||||
my $graphsdir = bz_locations()->{'graphsdir'};
|
||||
|
||||
# We want to set the permissions the same for all localconfig files
|
||||
# across all PROJECTs, so we do something special with $localconfig,
|
||||
# lower down in the permissions section.
|
||||
if ($ENV{PROJECT}) {
|
||||
$localconfig =~ s/\.\Q$ENV{PROJECT}\E$//;
|
||||
}
|
||||
|
||||
# Note: When being processed by checksetup, these have their permissions
|
||||
# set in this order: %all_dirs, %recurse_dirs, %all_files.
|
||||
#
|
||||
# Each is processed in alphabetical order of keys, so shorter keys
|
||||
# will have their permissions set before longer keys (thus setting
|
||||
# the permissions on parent directories before setting permissions
|
||||
# on their children).
|
||||
|
||||
# --- FILE PERMISSIONS (Non-created files) --- #
|
||||
my %files = (
|
||||
'*' => { perms => OWNER_WRITE },
|
||||
# Some .pl files are WS_EXECUTE because we want
|
||||
# users to be able to cron them or otherwise run
|
||||
# them as a secure user, like the webserver owner.
|
||||
'*.cgi' => { perms => WS_EXECUTE },
|
||||
'whineatnews.pl' => { perms => WS_EXECUTE },
|
||||
'collectstats.pl' => { perms => WS_EXECUTE },
|
||||
'importxml.pl' => { perms => WS_EXECUTE },
|
||||
'testserver.pl' => { perms => WS_EXECUTE },
|
||||
'whine.pl' => { perms => WS_EXECUTE },
|
||||
'email_in.pl' => { perms => WS_EXECUTE },
|
||||
'sanitycheck.pl' => { perms => WS_EXECUTE },
|
||||
'checksetup.pl' => { perms => OWNER_EXECUTE },
|
||||
'runtests.pl' => { perms => OWNER_EXECUTE },
|
||||
'jobqueue.pl' => { perms => OWNER_EXECUTE },
|
||||
'migrate.pl' => { perms => OWNER_EXECUTE },
|
||||
'install-module.pl' => { perms => OWNER_EXECUTE },
|
||||
|
||||
'Bugzilla.pm' => { perms => CGI_READ },
|
||||
"$localconfig*" => { perms => CGI_READ },
|
||||
'bugzilla.dtd' => { perms => WS_SERVE },
|
||||
'mod_perl.pl' => { perms => WS_SERVE },
|
||||
'robots.txt' => { perms => WS_SERVE },
|
||||
'.htaccess' => { perms => WS_SERVE },
|
||||
|
||||
'contrib/README' => { perms => OWNER_WRITE },
|
||||
'contrib/*/README' => { perms => OWNER_WRITE },
|
||||
'docs/bugzilla.ent' => { perms => OWNER_WRITE },
|
||||
'docs/makedocs.pl' => { perms => OWNER_EXECUTE },
|
||||
'docs/style.css' => { perms => WS_SERVE },
|
||||
'docs/*/rel_notes.txt' => { perms => WS_SERVE },
|
||||
'docs/*/README.docs' => { perms => OWNER_WRITE },
|
||||
"$datadir/params" => { perms => CGI_WRITE },
|
||||
"$datadir/old-params.txt" => { perms => OWNER_WRITE },
|
||||
"$extensionsdir/create.pl" => { perms => OWNER_EXECUTE },
|
||||
"$extensionsdir/*/*.pl" => { perms => WS_EXECUTE },
|
||||
);
|
||||
|
||||
# Directories that we want to set the perms on, but not
|
||||
# recurse through. These are directories we didn't create
|
||||
# in checkesetup.pl.
|
||||
my %non_recurse_dirs = (
|
||||
'.' => DIR_WS_SERVE,
|
||||
docs => DIR_WS_SERVE,
|
||||
);
|
||||
|
||||
# This sets the permissions for each item inside each of these
|
||||
# directories, including the directory itself.
|
||||
# 'CVS' directories are special, though, and are never readable by
|
||||
# the webserver.
|
||||
my %recurse_dirs = (
|
||||
# Writeable directories
|
||||
"$datadir/template" => { files => CGI_READ,
|
||||
dirs => DIR_CGI_OVERWRITE },
|
||||
$attachdir => { files => CGI_WRITE,
|
||||
dirs => DIR_CGI_WRITE },
|
||||
$webdotdir => { files => WS_SERVE,
|
||||
dirs => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE },
|
||||
$graphsdir => { files => WS_SERVE,
|
||||
dirs => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE },
|
||||
|
||||
# Readable directories
|
||||
"$datadir/mining" => { files => CGI_READ,
|
||||
dirs => DIR_CGI_READ },
|
||||
"$libdir/Bugzilla" => { files => CGI_READ,
|
||||
dirs => DIR_CGI_READ },
|
||||
$extlib => { files => CGI_READ,
|
||||
dirs => DIR_CGI_READ },
|
||||
$templatedir => { files => CGI_READ,
|
||||
dirs => DIR_CGI_READ },
|
||||
# Directories in the extensions/ dir are WS_SERVE so that
|
||||
# the web/ directories can be served by the web server.
|
||||
# But, for extra security, we deny direct webserver access to
|
||||
# the lib/ and template/ directories of extensions.
|
||||
$extensionsdir => { files => CGI_READ,
|
||||
dirs => DIR_WS_SERVE },
|
||||
"$extensionsdir/*/lib" => { files => CGI_READ,
|
||||
dirs => DIR_CGI_READ },
|
||||
"$extensionsdir/*/template" => { files => CGI_READ,
|
||||
dirs => DIR_CGI_READ },
|
||||
|
||||
# Content served directly by the webserver
|
||||
images => { files => WS_SERVE,
|
||||
dirs => DIR_WS_SERVE },
|
||||
js => { files => WS_SERVE,
|
||||
dirs => DIR_WS_SERVE },
|
||||
$skinsdir => { files => WS_SERVE,
|
||||
dirs => DIR_WS_SERVE },
|
||||
'docs/*/html' => { files => WS_SERVE,
|
||||
dirs => DIR_WS_SERVE },
|
||||
'docs/*/pdf' => { files => WS_SERVE,
|
||||
dirs => DIR_WS_SERVE },
|
||||
'docs/*/txt' => { files => WS_SERVE,
|
||||
dirs => DIR_WS_SERVE },
|
||||
'docs/*/images' => { files => WS_SERVE,
|
||||
dirs => DIR_WS_SERVE },
|
||||
"$extensionsdir/*/web" => { files => WS_SERVE,
|
||||
dirs => DIR_WS_SERVE },
|
||||
|
||||
# Directories only for the owner, not for the webserver.
|
||||
'.bzr' => { files => OWNER_WRITE,
|
||||
dirs => DIR_OWNER_WRITE },
|
||||
t => { files => OWNER_WRITE,
|
||||
dirs => DIR_OWNER_WRITE },
|
||||
xt => { files => OWNER_WRITE,
|
||||
dirs => DIR_OWNER_WRITE },
|
||||
'docs/lib' => { files => OWNER_WRITE,
|
||||
dirs => DIR_OWNER_WRITE },
|
||||
'docs/*/xml' => { files => OWNER_WRITE,
|
||||
dirs => DIR_OWNER_WRITE },
|
||||
'contrib' => { files => OWNER_EXECUTE,
|
||||
dirs => DIR_OWNER_WRITE, },
|
||||
);
|
||||
|
||||
# --- FILES TO CREATE --- #
|
||||
|
||||
# The name of each directory that we should actually *create*,
|
||||
# pointing at its default permissions.
|
||||
my %create_dirs = (
|
||||
# This is DIR_ALSO_WS_SERVE because it contains $webdotdir.
|
||||
$datadir => DIR_CGI_OVERWRITE | DIR_ALSO_WS_SERVE,
|
||||
# Directories that are read-only for cgi scripts
|
||||
"$datadir/mining" => DIR_CGI_READ,
|
||||
"$datadir/extensions" => DIR_CGI_READ,
|
||||
$extensionsdir => DIR_CGI_READ,
|
||||
# Directories that cgi scripts can write to.
|
||||
$attachdir => DIR_CGI_WRITE,
|
||||
$graphsdir => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE,
|
||||
$webdotdir => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE,
|
||||
# Directories that contain content served directly by the web server.
|
||||
"$skinsdir/custom" => DIR_WS_SERVE,
|
||||
"$skinsdir/contrib" => DIR_WS_SERVE,
|
||||
);
|
||||
|
||||
# The name of each file, pointing at its default permissions and
|
||||
# default contents.
|
||||
my %create_files = (
|
||||
"$datadir/extensions/additional" => { perms => CGI_READ,
|
||||
contents => '' },
|
||||
# We create this file so that it always has the right owner
|
||||
# and permissions. Otherwise, the webserver creates it as
|
||||
# owned by itself, which can cause problems if jobqueue.pl
|
||||
# or something else is not running as the webserver or root.
|
||||
"$datadir/mailer.testfile" => { perms => CGI_WRITE,
|
||||
contents => '' },
|
||||
);
|
||||
|
||||
# Because checksetup controls the creation of index.html separately
|
||||
# from all other files, it gets its very own hash.
|
||||
my %index_html = (
|
||||
'index.html' => { perms => WS_SERVE, contents => <<EOT
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Refresh" content="0; URL=index.cgi">
|
||||
</head>
|
||||
<body>
|
||||
<h1>I think you are looking for <a href="index.cgi">index.cgi</a></h1>
|
||||
</body>
|
||||
</html>
|
||||
EOT
|
||||
}
|
||||
);
|
||||
|
||||
# Because checksetup controls the .htaccess creation separately
|
||||
# by a localconfig variable, these go in a separate variable from
|
||||
# %create_files.
|
||||
#
|
||||
# Note that these get WS_SERVE as their permission
|
||||
# because they're *read* by the webserver, even though they're not
|
||||
# actually, themselves, served.
|
||||
my %htaccess = (
|
||||
"$attachdir/.htaccess" => { perms => WS_SERVE,
|
||||
contents => HT_DEFAULT_DENY },
|
||||
"$libdir/Bugzilla/.htaccess" => { perms => WS_SERVE,
|
||||
contents => HT_DEFAULT_DENY },
|
||||
"$extlib/.htaccess" => { perms => WS_SERVE,
|
||||
contents => HT_DEFAULT_DENY },
|
||||
"$templatedir/.htaccess" => { perms => WS_SERVE,
|
||||
contents => HT_DEFAULT_DENY },
|
||||
'contrib/.htaccess' => { perms => WS_SERVE,
|
||||
contents => HT_DEFAULT_DENY },
|
||||
't/.htaccess' => { perms => WS_SERVE,
|
||||
contents => HT_DEFAULT_DENY },
|
||||
'xt/.htaccess' => { perms => WS_SERVE,
|
||||
contents => HT_DEFAULT_DENY },
|
||||
"$datadir/.htaccess" => { perms => WS_SERVE,
|
||||
contents => HT_DEFAULT_DENY },
|
||||
|
||||
"$graphsdir/.htaccess" => { perms => WS_SERVE, contents => <<EOT
|
||||
# Allow access to .png and .gif files.
|
||||
<FilesMatch (\\.gif|\\.png)\$>
|
||||
Allow from all
|
||||
</FilesMatch>
|
||||
|
||||
# And no directory listings, either.
|
||||
Deny from all
|
||||
EOT
|
||||
},
|
||||
|
||||
"$webdotdir/.htaccess" => { perms => WS_SERVE, contents => <<EOT
|
||||
# Restrict access to .dot files to the public webdot server at research.att.com
|
||||
# if research.att.com ever changes their IP, or if you use a different
|
||||
# webdot server, you'll need to edit this
|
||||
<FilesMatch \\.dot\$>
|
||||
Allow from 192.20.225.0/24
|
||||
Deny from all
|
||||
</FilesMatch>
|
||||
|
||||
# Allow access to .png files created by a local copy of 'dot'
|
||||
<FilesMatch \\.png\$>
|
||||
Allow from all
|
||||
</FilesMatch>
|
||||
|
||||
# And no directory listings, either.
|
||||
Deny from all
|
||||
EOT
|
||||
},
|
||||
);
|
||||
|
||||
Bugzilla::Hook::process('install_filesystem', {
|
||||
files => \%files,
|
||||
create_dirs => \%create_dirs,
|
||||
non_recurse_dirs => \%non_recurse_dirs,
|
||||
recurse_dirs => \%recurse_dirs,
|
||||
create_files => \%create_files,
|
||||
htaccess => \%htaccess,
|
||||
});
|
||||
|
||||
my %all_files = (%create_files, %htaccess, %index_html, %files);
|
||||
my %all_dirs = (%create_dirs, %non_recurse_dirs);
|
||||
|
||||
return {
|
||||
create_dirs => \%create_dirs,
|
||||
recurse_dirs => \%recurse_dirs,
|
||||
all_dirs => \%all_dirs,
|
||||
|
||||
create_files => \%create_files,
|
||||
htaccess => \%htaccess,
|
||||
index_html => \%index_html,
|
||||
all_files => \%all_files,
|
||||
};
|
||||
}
|
||||
|
||||
sub update_filesystem {
|
||||
my ($params) = @_;
|
||||
my $fs = FILESYSTEM();
|
||||
my %dirs = %{$fs->{create_dirs}};
|
||||
my %files = %{$fs->{create_files}};
|
||||
|
||||
my $datadir = bz_locations->{'datadir'};
|
||||
my $graphsdir = bz_locations->{'graphsdir'};
|
||||
# If the graphs/ directory doesn't exist, we're upgrading from
|
||||
# a version old enough that we need to update the $datadir/mining
|
||||
# format.
|
||||
if (-d "$datadir/mining" && !-d $graphsdir) {
|
||||
_update_old_charts($datadir);
|
||||
}
|
||||
|
||||
# By sorting the dirs, we assure that shorter-named directories
|
||||
# (meaning parent directories) are always created before their
|
||||
# child directories.
|
||||
foreach my $dir (sort keys %dirs) {
|
||||
unless (-d $dir) {
|
||||
print "Creating $dir directory...\n";
|
||||
mkdir $dir or die "mkdir $dir failed: $!";
|
||||
# For some reason, passing in the permissions to "mkdir"
|
||||
# doesn't work right, but doing a "chmod" does.
|
||||
chmod $dirs{$dir}, $dir or warn "Cannot chmod $dir: $!";
|
||||
}
|
||||
}
|
||||
|
||||
# Move the testfile if we can't write to it, so that we can re-create
|
||||
# it with the correct permissions below.
|
||||
my $testfile = "$datadir/mailer.testfile";
|
||||
if (-e $testfile and !-w $testfile) {
|
||||
_rename_file($testfile, "$testfile.old");
|
||||
}
|
||||
|
||||
# If old-params.txt exists in the root directory, move it to datadir.
|
||||
my $oldparamsfile = "old_params.txt";
|
||||
if (-e $oldparamsfile) {
|
||||
_rename_file($oldparamsfile, "$datadir/$oldparamsfile");
|
||||
}
|
||||
|
||||
_create_files(%files);
|
||||
if ($params->{index_html}) {
|
||||
_create_files(%{$fs->{index_html}});
|
||||
}
|
||||
elsif (-e 'index.html') {
|
||||
my $templatedir = bz_locations()->{'templatedir'};
|
||||
print <<EOT;
|
||||
|
||||
*** It appears that you still have an old index.html hanging around.
|
||||
Either the contents of this file should be moved into a template and
|
||||
placed in the '$templatedir/en/custom' directory, or you should delete
|
||||
the file.
|
||||
|
||||
EOT
|
||||
}
|
||||
|
||||
# Delete old files that no longer need to exist
|
||||
|
||||
# 2001-04-29 jake@bugzilla.org - Remove oldemailtech
|
||||
# http://bugzilla.mozilla.org/show_bugs.cgi?id=71552
|
||||
if (-d 'shadow') {
|
||||
print "Removing shadow directory...\n";
|
||||
rmtree("shadow");
|
||||
}
|
||||
|
||||
if (-e "$datadir/versioncache") {
|
||||
print "Removing versioncache...\n";
|
||||
unlink "$datadir/versioncache";
|
||||
}
|
||||
|
||||
if (-e "$datadir/duplicates.rdf") {
|
||||
print "Removing duplicates.rdf...\n";
|
||||
unlink "$datadir/duplicates.rdf";
|
||||
unlink "$datadir/duplicates-old.rdf";
|
||||
}
|
||||
|
||||
if (-e "$datadir/duplicates") {
|
||||
print "Removing duplicates directory...\n";
|
||||
rmtree("$datadir/duplicates");
|
||||
}
|
||||
|
||||
_remove_empty_css_files();
|
||||
_convert_single_file_skins();
|
||||
}
|
||||
|
||||
sub _remove_empty_css_files {
|
||||
my $skinsdir = bz_locations()->{'skinsdir'};
|
||||
foreach my $css_file (glob("$skinsdir/custom/*.css"),
|
||||
glob("$skinsdir/contrib/*/*.css"))
|
||||
{
|
||||
_remove_empty_css($css_file);
|
||||
}
|
||||
}
|
||||
|
||||
# A simple helper for the update code that removes "empty" CSS files.
|
||||
sub _remove_empty_css {
|
||||
my ($file) = @_;
|
||||
my $basename = basename($file);
|
||||
my $empty_contents = <<EOT;
|
||||
/*
|
||||
* Custom rules for $basename.
|
||||
* The rules you put here override rules in that stylesheet.
|
||||
*/
|
||||
EOT
|
||||
if (length($empty_contents) == -s $file) {
|
||||
open(my $fh, '<', $file) or warn "$file: $!";
|
||||
my $file_contents;
|
||||
{ local $/; $file_contents = <$fh>; }
|
||||
if ($file_contents eq $empty_contents) {
|
||||
print install_string('file_remove', { name => $file }), "\n";
|
||||
unlink $file or warn "$file: $!";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
# We used to allow a single css file in the skins/contrib/ directory
|
||||
# to be a whole skin.
|
||||
sub _convert_single_file_skins {
|
||||
my $skinsdir = bz_locations()->{'skinsdir'};
|
||||
foreach my $skin_file (glob "$skinsdir/contrib/*.css") {
|
||||
my $dir_name = $skin_file;
|
||||
$dir_name =~ s/\.css$//;
|
||||
mkdir $dir_name or warn "$dir_name: $!";
|
||||
_rename_file($skin_file, "$dir_name/global.css");
|
||||
}
|
||||
}
|
||||
|
||||
sub create_htaccess {
|
||||
_create_files(%{FILESYSTEM()->{htaccess}});
|
||||
|
||||
# Repair old .htaccess files
|
||||
|
||||
my $webdot_dir = bz_locations()->{'webdotdir'};
|
||||
# The public webdot IP address changed.
|
||||
my $webdot = new IO::File("$webdot_dir/.htaccess", 'r')
|
||||
|| die "$webdot_dir/.htaccess: $!";
|
||||
my $webdot_data;
|
||||
{ local $/; $webdot_data = <$webdot>; }
|
||||
$webdot->close;
|
||||
if ($webdot_data =~ /192\.20\.225\.10/) {
|
||||
print "Repairing $webdot_dir/.htaccess...\n";
|
||||
$webdot_data =~ s/192\.20\.225\.10/192.20.225.0\/24/g;
|
||||
$webdot = new IO::File("$webdot_dir/.htaccess", 'w') || die $!;
|
||||
print $webdot $webdot_data;
|
||||
$webdot->close;
|
||||
}
|
||||
}
|
||||
|
||||
sub _rename_file {
|
||||
my ($from, $to) = @_;
|
||||
print install_string('file_rename', { from => $from, to => $to }), "\n";
|
||||
if (-e $to) {
|
||||
warn "$to already exists, not moving\n";
|
||||
}
|
||||
else {
|
||||
move($from, $to) or warn $!;
|
||||
}
|
||||
}
|
||||
|
||||
# A helper for the above functions.
|
||||
sub _create_files {
|
||||
my (%files) = @_;
|
||||
|
||||
# It's not necessary to sort these, but it does make the
|
||||
# output of checksetup.pl look a bit nicer.
|
||||
foreach my $file (sort keys %files) {
|
||||
unless (-e $file) {
|
||||
print "Creating $file...\n";
|
||||
my $info = $files{$file};
|
||||
my $fh = new IO::File($file, O_WRONLY | O_CREAT, $info->{perms})
|
||||
|| die $!;
|
||||
print $fh $info->{contents} if $info->{contents};
|
||||
$fh->close;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# If you ran a REALLY old version of Bugzilla, your chart files are in the
|
||||
# wrong format. This code is a little messy, because it's very old, and
|
||||
# when moving it into this module, I couldn't test it so I left it almost
|
||||
# completely alone.
|
||||
sub _update_old_charts {
|
||||
my ($datadir) = @_;
|
||||
print "Updating old chart storage format...\n";
|
||||
foreach my $in_file (glob("$datadir/mining/*")) {
|
||||
# Don't try and upgrade image or db files!
|
||||
next if (($in_file =~ /\.gif$/i) ||
|
||||
($in_file =~ /\.png$/i) ||
|
||||
($in_file =~ /\.db$/i) ||
|
||||
($in_file =~ /\.orig$/i));
|
||||
|
||||
rename("$in_file", "$in_file.orig") or next;
|
||||
open(IN, "<", "$in_file.orig") or next;
|
||||
open(OUT, '>', $in_file) or next;
|
||||
|
||||
# Fields in the header
|
||||
my @declared_fields;
|
||||
|
||||
# Fields we changed to half way through by mistake
|
||||
# This list comes from an old version of collectstats.pl
|
||||
# This part is only for people who ran later versions of 2.11 (devel)
|
||||
my @intermediate_fields = qw(DATE UNCONFIRMED NEW ASSIGNED REOPENED
|
||||
RESOLVED VERIFIED CLOSED);
|
||||
|
||||
# Fields we actually want (matches the current collectstats.pl)
|
||||
my @out_fields = qw(DATE NEW ASSIGNED REOPENED UNCONFIRMED RESOLVED
|
||||
VERIFIED CLOSED FIXED INVALID WONTFIX LATER REMIND
|
||||
DUPLICATE WORKSFORME MOVED);
|
||||
|
||||
while (<IN>) {
|
||||
if (/^# fields?: (.*)\s$/) {
|
||||
@declared_fields = map uc, (split /\||\r/, $1);
|
||||
print OUT "# fields: ", join('|', @out_fields), "\n";
|
||||
}
|
||||
elsif (/^(\d+\|.*)/) {
|
||||
my @data = split(/\||\r/, $1);
|
||||
my %data;
|
||||
if (@data == @declared_fields) {
|
||||
# old format
|
||||
for my $i (0 .. $#declared_fields) {
|
||||
$data{$declared_fields[$i]} = $data[$i];
|
||||
}
|
||||
}
|
||||
elsif (@data == @intermediate_fields) {
|
||||
# Must have changed over at this point
|
||||
for my $i (0 .. $#intermediate_fields) {
|
||||
$data{$intermediate_fields[$i]} = $data[$i];
|
||||
}
|
||||
}
|
||||
elsif (@data == @out_fields) {
|
||||
# This line's fine - it has the right number of entries
|
||||
for my $i (0 .. $#out_fields) {
|
||||
$data{$out_fields[$i]} = $data[$i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
print "Oh dear, input line $. of $in_file had " .
|
||||
scalar(@data) . " fields\nThis was unexpected.",
|
||||
" You may want to check your data files.\n";
|
||||
}
|
||||
|
||||
print OUT join('|',
|
||||
map { defined ($data{$_}) ? ($data{$_}) : "" } @out_fields),
|
||||
"\n";
|
||||
}
|
||||
else {
|
||||
print OUT;
|
||||
}
|
||||
}
|
||||
|
||||
close(IN);
|
||||
close(OUT);
|
||||
}
|
||||
}
|
||||
|
||||
sub fix_file_permissions {
|
||||
my ($file) = @_;
|
||||
return if ON_WINDOWS;
|
||||
my $perms = FILESYSTEM()->{all_files}->{$file}->{perms};
|
||||
# Note that _get_owner_and_group is always silent here.
|
||||
my ($owner_id, $group_id) = _get_owner_and_group();
|
||||
_fix_perms($file, $owner_id, $group_id, $perms);
|
||||
}
|
||||
|
||||
sub fix_all_file_permissions {
|
||||
my ($output) = @_;
|
||||
|
||||
# _get_owner_and_group also checks that the webservergroup is valid.
|
||||
my ($owner_id, $group_id) = _get_owner_and_group($output);
|
||||
|
||||
return if ON_WINDOWS;
|
||||
|
||||
my $fs = FILESYSTEM();
|
||||
my %files = %{$fs->{all_files}};
|
||||
my %dirs = %{$fs->{all_dirs}};
|
||||
my %recurse_dirs = %{$fs->{recurse_dirs}};
|
||||
|
||||
print get_text('install_file_perms_fix') . "\n" if $output;
|
||||
|
||||
foreach my $dir (sort keys %dirs) {
|
||||
next unless -d $dir;
|
||||
_fix_perms($dir, $owner_id, $group_id, $dirs{$dir});
|
||||
}
|
||||
|
||||
foreach my $pattern (sort keys %recurse_dirs) {
|
||||
my $perms = $recurse_dirs{$pattern};
|
||||
# %recurse_dirs supports globs
|
||||
foreach my $dir (glob $pattern) {
|
||||
next unless -d $dir;
|
||||
_fix_perms_recursively($dir, $owner_id, $group_id, $perms);
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $file (sort keys %files) {
|
||||
# %files supports globs
|
||||
foreach my $filename (glob $file) {
|
||||
# Don't touch directories.
|
||||
next if -d $filename || !-e $filename;
|
||||
_fix_perms($filename, $owner_id, $group_id,
|
||||
$files{$file}->{perms});
|
||||
}
|
||||
}
|
||||
|
||||
_fix_cvs_dirs($owner_id, '.');
|
||||
}
|
||||
|
||||
sub _get_owner_and_group {
|
||||
my ($output) = @_;
|
||||
my $group_id = _check_web_server_group($output);
|
||||
return () if ON_WINDOWS;
|
||||
|
||||
my $owner_id = POSIX::getuid();
|
||||
$group_id = POSIX::getgid() unless defined $group_id;
|
||||
return ($owner_id, $group_id);
|
||||
}
|
||||
|
||||
# A helper for fix_all_file_permissions
|
||||
sub _fix_cvs_dirs {
|
||||
my ($owner_id, $dir) = @_;
|
||||
my $owner_gid = POSIX::getgid();
|
||||
find({ no_chdir => 1, wanted => sub {
|
||||
my $name = $File::Find::name;
|
||||
if ($File::Find::dir =~ /\/CVS/ || $_ eq '.cvsignore'
|
||||
|| (-d $name && $_ =~ /CVS$/))
|
||||
{
|
||||
my $perms = 0600;
|
||||
if (-d $name) {
|
||||
$perms = 0700;
|
||||
}
|
||||
_fix_perms($name, $owner_id, $owner_gid, $perms);
|
||||
}
|
||||
}}, $dir);
|
||||
}
|
||||
|
||||
sub _fix_perms {
|
||||
my ($name, $owner, $group, $perms) = @_;
|
||||
#printf ("Changing $name to %o\n", $perms);
|
||||
|
||||
# The webserver should never try to chown files.
|
||||
if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
|
||||
chown $owner, $group, $name
|
||||
or warn install_string('chown_failed', { path => $name,
|
||||
error => $! }) . "\n";
|
||||
}
|
||||
chmod $perms, $name
|
||||
or warn install_string('chmod_failed', { path => $name,
|
||||
error => $! }) . "\n";
|
||||
}
|
||||
|
||||
sub _fix_perms_recursively {
|
||||
my ($dir, $owner_id, $group_id, $perms) = @_;
|
||||
# Set permissions on the directory itself.
|
||||
_fix_perms($dir, $owner_id, $group_id, $perms->{dirs});
|
||||
# Now recurse through the directory and set the correct permissions
|
||||
# on subdirectories and files.
|
||||
find({ no_chdir => 1, wanted => sub {
|
||||
my $name = $File::Find::name;
|
||||
if (-d $name) {
|
||||
_fix_perms($name, $owner_id, $group_id, $perms->{dirs});
|
||||
}
|
||||
else {
|
||||
_fix_perms($name, $owner_id, $group_id, $perms->{files});
|
||||
}
|
||||
}}, $dir);
|
||||
}
|
||||
|
||||
sub _check_web_server_group {
|
||||
my ($output) = @_;
|
||||
|
||||
my $group = Bugzilla->localconfig->{'webservergroup'};
|
||||
my $filename = bz_locations()->{'localconfig'};
|
||||
my $group_id;
|
||||
|
||||
# If we are on Windows, webservergroup does nothing
|
||||
if (ON_WINDOWS && $group && $output) {
|
||||
print "\n\n" . get_text('install_webservergroup_windows') . "\n\n";
|
||||
}
|
||||
|
||||
# If we're not on Windows, make sure that webservergroup isn't
|
||||
# empty.
|
||||
elsif (!ON_WINDOWS && !$group && $output) {
|
||||
print "\n\n" . get_text('install_webservergroup_empty') . "\n\n";
|
||||
}
|
||||
|
||||
# If we're not on Windows, make sure we are actually a member of
|
||||
# the webservergroup.
|
||||
elsif (!ON_WINDOWS && $group) {
|
||||
$group_id = getgrnam($group);
|
||||
ThrowCodeError('invalid_webservergroup', { group => $group })
|
||||
unless defined $group_id;
|
||||
|
||||
# If on unix, see if we need to print a warning about a webservergroup
|
||||
# that we can't chgrp to
|
||||
if ($output && $< != 0 && !grep($_ eq $group_id, split(" ", $)))) {
|
||||
print "\n\n" . get_text('install_webservergroup_not_in') . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $group_id;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Install::Filesystem - Fix up the filesystem during
|
||||
installation.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is used primarily by L<checksetup.pl> to modify the
|
||||
filesystem during installation, including creating the data/ directory.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=over
|
||||
|
||||
=item C<update_filesystem({ index_html => 0 })>
|
||||
|
||||
Description: Creates all the directories and files that Bugzilla
|
||||
needs to function but doesn't ship with. Also does
|
||||
any updates to these files as necessary during an
|
||||
upgrade.
|
||||
|
||||
Params: C<index_html> - Whether or not we should create
|
||||
the F<index.html> file.
|
||||
|
||||
Returns: nothing
|
||||
|
||||
=item C<create_htaccess()>
|
||||
|
||||
Description: Creates all of the .htaccess files for Apache,
|
||||
in the various Bugzilla directories. Also updates
|
||||
the .htaccess files if they need updating.
|
||||
|
||||
Params: none
|
||||
|
||||
Returns: nothing
|
||||
|
||||
=item C<fix_all_file_permissions($output)>
|
||||
|
||||
Description: Sets all the file permissions on all of Bugzilla's files
|
||||
to what they should be. Note that permissions are different
|
||||
depending on whether or not C<$webservergroup> is set
|
||||
in F<localconfig>.
|
||||
|
||||
Params: C<$output> - C<true> if you want this function to print
|
||||
out information about what it's doing.
|
||||
|
||||
Returns: nothing
|
||||
|
||||
=item C<fix_file_permissions>
|
||||
|
||||
Given the name of a file, its permissions will be fixed according to
|
||||
how they are supposed to be set in Bugzilla's current configuration.
|
||||
If it fails to set the permissions, a warning will be printed to STDERR.
|
||||
|
||||
=back
|
||||
@@ -1,515 +0,0 @@
|
||||
# -*- 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 Initial Developer of the Original Code is Everything Solved.
|
||||
# Portions created by Everything Solved are Copyright (C) 2006
|
||||
# Everything Solved. All Rights Reserved.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Install::Localconfig;
|
||||
|
||||
# NOTE: This package may "use" any modules that it likes. However,
|
||||
# all functions in this package should assume that:
|
||||
#
|
||||
# * The data/ directory does not exist.
|
||||
# * Templates are not available.
|
||||
# * Files do not have the correct permissions
|
||||
# * The database is not up to date
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Install::Util qw(bin_loc);
|
||||
use Bugzilla::Util qw(generate_random_password);
|
||||
|
||||
use Data::Dumper;
|
||||
use File::Basename qw(dirname);
|
||||
use IO::File;
|
||||
use Safe;
|
||||
|
||||
use base qw(Exporter);
|
||||
|
||||
our @EXPORT_OK = qw(
|
||||
read_localconfig
|
||||
update_localconfig
|
||||
);
|
||||
|
||||
use constant LOCALCONFIG_VARS => (
|
||||
{
|
||||
name => 'create_htaccess',
|
||||
default => 1,
|
||||
desc => <<EOT
|
||||
# If you are using Apache as your web server, Bugzilla can create .htaccess
|
||||
# files for you that will instruct Apache not to serve files that shouldn't
|
||||
# be accessed from the web browser (like your local configuration data and non-cgi
|
||||
# executable files). For this to work, the directory your Bugzilla
|
||||
# installation is in must be within the jurisdiction of a <Directory> block
|
||||
# in the httpd.conf file that has 'AllowOverride Limit' in it. If it has
|
||||
# 'AllowOverride All' or other options with Limit, that's fine.
|
||||
# (Older Apache installations may use an access.conf file to store these
|
||||
# <Directory> blocks.)
|
||||
# If this is set to 1, Bugzilla will create these files if they don't exist.
|
||||
# If this is set to 0, Bugzilla will not create these files.
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'webservergroup',
|
||||
default => ON_WINDOWS ? '' : 'apache',
|
||||
desc => q{# Usually, this is the group your web server runs as.
|
||||
# If you have a Windows box, ignore this setting.
|
||||
# If you have use_suexec switched on below, this is the group Apache switches
|
||||
# to in order to run Bugzilla scripts.
|
||||
# If you do not have access to the group your scripts will run under,
|
||||
# set this to "". If you do set this to "", then your Bugzilla installation
|
||||
# will be _VERY_ insecure, because some files will be world readable/writable,
|
||||
# and so anyone who can get local access to your machine can do whatever they
|
||||
# want. You should only have this set to "" if this is a testing installation
|
||||
# and you cannot set this up any other way. YOU HAVE BEEN WARNED!
|
||||
# If you set this to anything other than "", you will need to run checksetup.pl
|
||||
# as} . ROOT_USER . qq{, or as a user who is a member of the specified group.\n}
|
||||
},
|
||||
{
|
||||
name => 'use_suexec',
|
||||
default => 0,
|
||||
desc => <<EOT
|
||||
# Set this if Bugzilla runs in an Apache SuexecUserGroup environment.
|
||||
# (If your web server runs control panel software (cPanel, Plesk or similar),
|
||||
# or if your Bugzilla is to run in a shared hosting environment, then you are
|
||||
# almost certainly in an Apache SuexecUserGroup environment.)
|
||||
# If you have a Windows box, ignore this setting.
|
||||
# If set to 0, Bugzilla will set file permissions as tightly as possible.
|
||||
# If set to 1, Bugzilla will set file permissions so that it may work in an
|
||||
# SuexecUserGroup environment. The difference is that static files (CSS,
|
||||
# JavaScript and so on) will receive world read permissions.
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'db_driver',
|
||||
default => 'mysql',
|
||||
desc => <<EOT
|
||||
# What SQL database to use. Default is mysql. List of supported databases
|
||||
# can be obtained by listing Bugzilla/DB directory - every module corresponds
|
||||
# to one supported database and the name corresponds to a driver name.
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'db_host',
|
||||
default => 'localhost',
|
||||
desc =>
|
||||
"# The DNS name of the host that the database server runs on.\n"
|
||||
},
|
||||
{
|
||||
name => 'db_name',
|
||||
default => 'bugs',
|
||||
desc => "# The name of the database\n"
|
||||
},
|
||||
{
|
||||
name => 'db_user',
|
||||
default => 'bugs',
|
||||
desc => "# Who we connect to the database as.\n"
|
||||
},
|
||||
{
|
||||
name => 'db_pass',
|
||||
default => '',
|
||||
desc => <<EOT
|
||||
# Enter your database password here. It's normally advisable to specify
|
||||
# a password for your bugzilla database user.
|
||||
# If you use apostrophe (') or a backslash (\\) in your password, you'll
|
||||
# need to escape it by preceding it with a '\\' character. (\\') or (\\)
|
||||
# (Far simpler just not to use those characters.)
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'db_port',
|
||||
default => 0,
|
||||
desc => <<EOT
|
||||
# Sometimes the database server is running on a non-standard port. If that's
|
||||
# the case for your database server, set this to the port number that your
|
||||
# database server is running on. Setting this to 0 means "use the default
|
||||
# port for my database server."
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'db_sock',
|
||||
default => '',
|
||||
desc => <<EOT
|
||||
# MySQL Only: Enter a path to the unix socket for MySQL. If this is
|
||||
# blank, then MySQL's compiled-in default will be used. You probably
|
||||
# want that.
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'db_check',
|
||||
default => 1,
|
||||
desc => <<EOT
|
||||
# Should checksetup.pl try to verify that your database setup is correct?
|
||||
# (with some combinations of database servers/Perl modules/moonphase this
|
||||
# doesn't work)
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'index_html',
|
||||
default => 0,
|
||||
desc => <<EOT
|
||||
# With the introduction of a configurable index page using the
|
||||
# template toolkit, Bugzilla's main index page is now index.cgi.
|
||||
# Most web servers will allow you to use index.cgi as a directory
|
||||
# index, and many come preconfigured that way, but if yours doesn't
|
||||
# then you'll need an index.html file that provides redirection
|
||||
# to index.cgi. Setting \$index_html to 1 below will allow
|
||||
# checksetup.pl to create one for you if it doesn't exist.
|
||||
# NOTE: checksetup.pl will not replace an existing file, so if you
|
||||
# wish to have checksetup.pl create one for you, you must
|
||||
# make sure that index.html doesn't already exist
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'cvsbin',
|
||||
default => \&_get_default_cvsbin,
|
||||
desc => <<EOT
|
||||
# For some optional functions of Bugzilla (such as the pretty-print patch
|
||||
# viewer), we need the cvs binary to access files and revisions.
|
||||
# Because it's possible that this program is not in your path, you can specify
|
||||
# its location here. Please specify the full path to the executable.
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'interdiffbin',
|
||||
default => \&_get_default_interdiffbin,
|
||||
desc => <<EOT
|
||||
# For some optional functions of Bugzilla (such as the pretty-print patch
|
||||
# viewer), we need the interdiff binary to make diffs between two patches.
|
||||
# Because it's possible that this program is not in your path, you can specify
|
||||
# its location here. Please specify the full path to the executable.
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'diffpath',
|
||||
default => \&_get_default_diffpath,
|
||||
desc => <<EOT
|
||||
# The interdiff feature needs diff, so we have to have that path.
|
||||
# Please specify the directory name only; do not use trailing slash.
|
||||
EOT
|
||||
},
|
||||
{
|
||||
name => 'site_wide_secret',
|
||||
# 64 characters is roughly the equivalent of a 384-bit key, which
|
||||
# is larger than anybody would ever be able to brute-force.
|
||||
default => sub { generate_random_password(64) },
|
||||
desc => <<EOT
|
||||
# This secret key is used by your installation for the creation and
|
||||
# validation of encrypted tokens to prevent unsolicited changes,
|
||||
# such as bug changes. A random string is generated by default.
|
||||
# It's very important that this key is kept secret. It also must be
|
||||
# very long.
|
||||
EOT
|
||||
},
|
||||
);
|
||||
|
||||
sub read_localconfig {
|
||||
my ($include_deprecated) = @_;
|
||||
my $filename = bz_locations()->{'localconfig'};
|
||||
|
||||
my %localconfig;
|
||||
if (-e $filename) {
|
||||
my $s = new Safe;
|
||||
# Some people like to store their database password in another file.
|
||||
$s->permit('dofile');
|
||||
|
||||
$s->rdo($filename);
|
||||
if ($@ || $!) {
|
||||
my $err_msg = $@ ? $@ : $!;
|
||||
die <<EOT;
|
||||
An error has occurred while reading your 'localconfig' file. The text of
|
||||
the error message is:
|
||||
|
||||
$err_msg
|
||||
|
||||
Please fix the error in your 'localconfig' file. Alternately, rename your
|
||||
'localconfig' file, rerun checksetup.pl, and re-enter your answers.
|
||||
|
||||
\$ mv -f localconfig localconfig.old
|
||||
\$ ./checksetup.pl
|
||||
EOT
|
||||
}
|
||||
|
||||
my @read_symbols;
|
||||
if ($include_deprecated) {
|
||||
# First we have to get the whole symbol table
|
||||
my $safe_root = $s->root;
|
||||
my %safe_package;
|
||||
{ no strict 'refs'; %safe_package = %{$safe_root . "::"}; }
|
||||
# And now we read the contents of every var in the symbol table.
|
||||
# However:
|
||||
# * We only include symbols that start with an alphanumeric
|
||||
# character. This excludes symbols like "_<./localconfig"
|
||||
# that show up in some perls.
|
||||
# * We ignore the INC symbol, which exists in every package.
|
||||
# * Perl 5.10 imports a lot of random symbols that all
|
||||
# contain "::", and we want to ignore those.
|
||||
@read_symbols = grep { /^[A-Za-z0-1]/ and !/^INC$/ and !/::/ }
|
||||
(keys %safe_package);
|
||||
}
|
||||
else {
|
||||
@read_symbols = map($_->{name}, LOCALCONFIG_VARS);
|
||||
}
|
||||
foreach my $var (@read_symbols) {
|
||||
my $glob = $s->varglob($var);
|
||||
# We can't get the type of a variable out of a Safe automatically.
|
||||
# We can only get the glob itself. So we figure out its type this
|
||||
# way, by trying first a scalar, then an array, then a hash.
|
||||
#
|
||||
# The interesting thing is that this converts all deprecated
|
||||
# array or hash vars into hashrefs or arrayrefs, but that's
|
||||
# fine since as I write this all modern localconfig vars are
|
||||
# actually scalars.
|
||||
if (defined $$glob) {
|
||||
$localconfig{$var} = $$glob;
|
||||
}
|
||||
elsif (@$glob) {
|
||||
$localconfig{$var} = \@$glob;
|
||||
}
|
||||
elsif (%$glob) {
|
||||
$localconfig{$var} = \%$glob;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return \%localconfig;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# This is quite tricky. But fun!
|
||||
#
|
||||
# First we read the file 'localconfig'. Then we check if the variables we
|
||||
# need are defined. If not, we will append the new settings to
|
||||
# localconfig, instruct the user to check them, and stop.
|
||||
#
|
||||
# Why do it this way?
|
||||
#
|
||||
# Assume we will enhance Bugzilla and eventually more local configuration
|
||||
# stuff arises on the horizon.
|
||||
#
|
||||
# But the file 'localconfig' is not in the Bugzilla CVS or tarfile. You
|
||||
# know, we never want to overwrite your own version of 'localconfig', so
|
||||
# we can't put it into the CVS/tarfile, can we?
|
||||
#
|
||||
# Now, when we need a new variable, we simply add the necessary stuff to
|
||||
# LOCALCONFIG_VARS. When the user gets the new version of Bugzilla from CVS and
|
||||
# runs checksetup, it finds out "Oh, there is something new". Then it adds
|
||||
# some default value to the user's local setup and informs the user to
|
||||
# check that to see if it is what the user wants.
|
||||
#
|
||||
# Cute, ey?
|
||||
#
|
||||
sub update_localconfig {
|
||||
my ($params) = @_;
|
||||
|
||||
my $output = $params->{output} || 0;
|
||||
my $answer = Bugzilla->installation_answers;
|
||||
my $localconfig = read_localconfig('include deprecated');
|
||||
|
||||
my @new_vars;
|
||||
foreach my $var (LOCALCONFIG_VARS) {
|
||||
my $name = $var->{name};
|
||||
my $value = $localconfig->{$name};
|
||||
# Regenerate site_wide_secret if it was made by our old, weak
|
||||
# generate_random_password. Previously we used to generate
|
||||
# a 256-character string for site_wide_secret.
|
||||
$value = undef if ($name eq 'site_wide_secret' and defined $value
|
||||
and length($value) == 256);
|
||||
|
||||
if (!defined $value) {
|
||||
push(@new_vars, $name);
|
||||
$var->{default} = &{$var->{default}} if ref($var->{default}) eq 'CODE';
|
||||
if (exists $answer->{$name}) {
|
||||
$localconfig->{$name} = $answer->{$name};
|
||||
}
|
||||
else {
|
||||
$localconfig->{$name} = $var->{default};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$localconfig->{'interdiffbin'} && $output) {
|
||||
print <<EOT
|
||||
|
||||
OPTIONAL NOTE: If you want to be able to use the 'difference between two
|
||||
patches' feature of Bugzilla (which requires the PatchReader Perl module
|
||||
as well), you should install patchutils from:
|
||||
|
||||
http://cyberelk.net/tim/patchutils/
|
||||
|
||||
EOT
|
||||
}
|
||||
|
||||
my @old_vars;
|
||||
foreach my $var (keys %$localconfig) {
|
||||
push(@old_vars, $var) if !grep($_->{name} eq $var, LOCALCONFIG_VARS);
|
||||
}
|
||||
|
||||
my $filename = bz_locations->{'localconfig'};
|
||||
|
||||
# Move any custom or old variables into a separate file.
|
||||
if (scalar @old_vars) {
|
||||
my $filename_old = "$filename.old";
|
||||
open(my $old_file, ">>", $filename_old) || die "$filename_old: $!";
|
||||
local $Data::Dumper::Purity = 1;
|
||||
foreach my $var (@old_vars) {
|
||||
print $old_file Data::Dumper->Dump([$localconfig->{$var}],
|
||||
["*$var"]) . "\n\n";
|
||||
}
|
||||
close $old_file;
|
||||
my $oldstuff = join(', ', @old_vars);
|
||||
print <<EOT
|
||||
|
||||
The following variables are no longer used in $filename, and
|
||||
have been moved to $filename_old: $oldstuff
|
||||
|
||||
EOT
|
||||
}
|
||||
|
||||
# Re-write localconfig
|
||||
open(my $fh, ">", $filename) || die "$filename: $!";
|
||||
foreach my $var (LOCALCONFIG_VARS) {
|
||||
print $fh "\n", $var->{desc},
|
||||
Data::Dumper->Dump([$localconfig->{$var->{name}}],
|
||||
["*$var->{name}"]);
|
||||
}
|
||||
|
||||
if (@new_vars) {
|
||||
my $newstuff = join(', ', @new_vars);
|
||||
print <<EOT;
|
||||
|
||||
This version of Bugzilla contains some variables that you may want to
|
||||
change and adapt to your local settings. Please edit the file
|
||||
$filename and rerun checksetup.pl.
|
||||
|
||||
The following variables are new to $filename since you last ran
|
||||
checksetup.pl: $newstuff
|
||||
|
||||
EOT
|
||||
exit;
|
||||
}
|
||||
|
||||
# Reset the cache for Bugzilla->localconfig so that it will be re-read
|
||||
delete Bugzilla->request_cache->{localconfig};
|
||||
|
||||
return { old_vars => \@old_vars, new_vars => \@new_vars };
|
||||
}
|
||||
|
||||
sub _get_default_cvsbin { return bin_loc('cvs') }
|
||||
sub _get_default_interdiffbin { return bin_loc('interdiff') }
|
||||
sub _get_default_diffpath {
|
||||
my $diff_bin = bin_loc('diff');
|
||||
return dirname($diff_bin);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Install::Localconfig - Functions and variables dealing
|
||||
with the manipulation and creation of the F<localconfig> file.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Install::Requirements qw(update_localconfig);
|
||||
update_localconfig({ output => 1 });
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is used primarily by L<checksetup.pl> to create and
|
||||
modify the localconfig file. Most scripts should use L<Bugzilla/localconfig>
|
||||
to access localconfig variables.
|
||||
|
||||
=head1 CONSTANTS
|
||||
|
||||
=over
|
||||
|
||||
=item C<LOCALCONFIG_VARS>
|
||||
|
||||
An array of hashrefs. These hashrefs contain three keys:
|
||||
|
||||
name - The name of the variable.
|
||||
default - The default value for the variable. Should always be
|
||||
something that can fit in a scalar.
|
||||
desc - Additional text to put in localconfig before the variable
|
||||
definition. Must end in a newline. Each line should start
|
||||
with "#" unless you have some REALLY good reason not
|
||||
to do that.
|
||||
|
||||
=item C<OLD_LOCALCONFIG_VARS>
|
||||
|
||||
An array of names of variables. If C<update_localconfig> finds these
|
||||
variables defined in localconfig, it will print out a warning.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=over
|
||||
|
||||
=item C<read_localconfig>
|
||||
|
||||
=over
|
||||
|
||||
=item B<Description>
|
||||
|
||||
Reads the localconfig file and returns all valid values in a hashref.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
||||
=item C<$include_deprecated>
|
||||
|
||||
C<true> if you want the returned hashref to include *any* variable
|
||||
currently defined in localconfig, even if it doesn't exist in
|
||||
C<LOCALCONFIG_VARS>. Generally this is is only for use
|
||||
by L</update_localconfig>.
|
||||
|
||||
=back
|
||||
|
||||
=item B<Returns>
|
||||
|
||||
A hashref of the localconfig variables. If an array is defined in
|
||||
localconfig, it will be an arrayref in the returned hash. If a
|
||||
hash is defined, it will be a hashref in the returned hash.
|
||||
Only includes variables specified in C<LOCALCONFIG_VARS>, unless
|
||||
C<$include_deprecated> is true.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
=item C<update_localconfig>
|
||||
|
||||
Description: Adds any new variables to localconfig that aren't
|
||||
currently defined there. Also optionally prints out
|
||||
a message about vars that *should* be there and aren't.
|
||||
Exits the program if it adds any new vars.
|
||||
|
||||
Params: C<$output> - C<true> if the function should display informational
|
||||
output and warnings. It will always display errors or
|
||||
any message which would cause program execution to halt.
|
||||
|
||||
Returns: A hashref, with C<old_vals> being an array of names of variables
|
||||
that were removed, and C<new_vals> being an array of names
|
||||
of variables that were added to localconfig.
|
||||
|
||||
=back
|
||||
@@ -1,887 +0,0 @@
|
||||
# -*- 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): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
# Marc Schumann <wurblzap@gmail.com>
|
||||
|
||||
package Bugzilla::Install::Requirements;
|
||||
|
||||
# NOTE: This package MUST NOT "use" any Bugzilla modules other than
|
||||
# Bugzilla::Constants, anywhere. We may "use" standard perl modules.
|
||||
#
|
||||
# Subroutines may "require" and "import" from modules, but they
|
||||
# MUST NOT "use."
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Install::Util qw(vers_cmp install_string bin_loc
|
||||
extension_requirement_packages);
|
||||
use List::Util qw(max);
|
||||
use Safe;
|
||||
use Term::ANSIColor;
|
||||
|
||||
# Return::Value 1.666002 pollutes the error log with warnings about this
|
||||
# deprecated module. We have to set NO_CLUCK = 1 before loading Email::Send
|
||||
# in have_vers() to disable these warnings.
|
||||
BEGIN {
|
||||
$Return::Value::NO_CLUCK = 1;
|
||||
}
|
||||
|
||||
use base qw(Exporter);
|
||||
our @EXPORT = qw(
|
||||
REQUIRED_MODULES
|
||||
OPTIONAL_MODULES
|
||||
FEATURE_FILES
|
||||
|
||||
check_requirements
|
||||
check_graphviz
|
||||
have_vers
|
||||
install_command
|
||||
map_files_to_features
|
||||
);
|
||||
|
||||
# This is how many *'s are in the top of each "box" message printed
|
||||
# by checksetup.pl.
|
||||
use constant TABLE_WIDTH => 71;
|
||||
|
||||
# Optional Apache modules that have no Perl component to them.
|
||||
# If these are installed, Bugzilla has additional functionality.
|
||||
#
|
||||
# The keys are the names of the modules, the values are what the module
|
||||
# is called in the output of "apachectl -t -D DUMP_MODULES".
|
||||
use constant APACHE_MODULES => {
|
||||
mod_headers => 'headers_module',
|
||||
mod_env => 'env_module',
|
||||
mod_expires => 'expires_module',
|
||||
};
|
||||
|
||||
# These are all of the binaries that we could possibly use that can
|
||||
# give us info about which Apache modules are installed.
|
||||
# If we can't use "apachectl", the "httpd" binary itself takes the same
|
||||
# parameters. Note that on Debian and Gentoo, there is an "apache2ctl",
|
||||
# but it takes different parameters on each of those two distros, so we
|
||||
# don't use apache2ctl.
|
||||
use constant APACHE => qw(apachectl httpd apache2 apache);
|
||||
|
||||
# If we don't find any of the above binaries in the normal PATH,
|
||||
# these are extra places we look.
|
||||
use constant APACHE_PATH => [qw(
|
||||
/usr/sbin
|
||||
/usr/local/sbin
|
||||
/usr/libexec
|
||||
/usr/local/libexec
|
||||
)];
|
||||
|
||||
# The below two constants are subroutines so that they can implement
|
||||
# a hook. Other than that they are actually constants.
|
||||
|
||||
# "package" is the perl package we're checking for. "module" is the name
|
||||
# of the actual module we load with "require" to see if the package is
|
||||
# installed or not. "version" is the version we need, or 0 if we'll accept
|
||||
# any version.
|
||||
#
|
||||
# "blacklist" is an arrayref of regular expressions that describe versions that
|
||||
# are 'blacklisted'--that is, even if the version is high enough, Bugzilla
|
||||
# will refuse to say that it's OK to run with that version.
|
||||
sub REQUIRED_MODULES {
|
||||
my $perl_ver = sprintf('%vd', $^V);
|
||||
my @modules = (
|
||||
{
|
||||
package => 'CGI.pm',
|
||||
module => 'CGI',
|
||||
# 3.51 fixes a security problem that affects Bugzilla.
|
||||
# (bug 591165)
|
||||
version => '3.51',
|
||||
},
|
||||
{
|
||||
package => 'Digest-SHA',
|
||||
module => 'Digest::SHA',
|
||||
version => 0
|
||||
},
|
||||
{
|
||||
package => 'TimeDate',
|
||||
module => 'Date::Format',
|
||||
version => '2.21'
|
||||
},
|
||||
# 0.28 fixed some important bugs in DateTime.
|
||||
{
|
||||
package => 'DateTime',
|
||||
module => 'DateTime',
|
||||
version => '0.28'
|
||||
},
|
||||
# 0.79 is required to work on Windows Vista and Windows Server 2008.
|
||||
# As correctly detecting the flavor of Windows is not easy,
|
||||
# we require this version for all Windows installations.
|
||||
# 0.71 fixes a major bug affecting all platforms.
|
||||
{
|
||||
package => 'DateTime-TimeZone',
|
||||
module => 'DateTime::TimeZone',
|
||||
version => ON_WINDOWS ? '0.79' : '0.71'
|
||||
},
|
||||
{
|
||||
package => 'DBI',
|
||||
module => 'DBI',
|
||||
version => (vers_cmp($perl_ver, '5.13.3') > -1) ? '1.614' : '1.41'
|
||||
},
|
||||
# 2.22 fixes various problems related to UTF8 strings in hash keys,
|
||||
# as well as line endings on Windows.
|
||||
{
|
||||
package => 'Template-Toolkit',
|
||||
module => 'Template',
|
||||
version => '2.22'
|
||||
},
|
||||
{
|
||||
package => 'Email-Send',
|
||||
module => 'Email::Send',
|
||||
version => ON_WINDOWS ? '2.16' : '2.00',
|
||||
blacklist => ['^2\.196$']
|
||||
},
|
||||
{
|
||||
package => 'Email-MIME',
|
||||
module => 'Email::MIME',
|
||||
# This fixes a memory leak in walk_parts that affected jobqueue.pl.
|
||||
version => '1.904'
|
||||
},
|
||||
{
|
||||
package => 'URI',
|
||||
module => 'URI',
|
||||
version => 0
|
||||
},
|
||||
{
|
||||
package => 'List-MoreUtils',
|
||||
module => 'List::MoreUtils',
|
||||
version => 0.22,
|
||||
},
|
||||
);
|
||||
|
||||
my $extra_modules = _get_extension_requirements('REQUIRED_MODULES');
|
||||
push(@modules, @$extra_modules);
|
||||
return \@modules;
|
||||
};
|
||||
|
||||
sub OPTIONAL_MODULES {
|
||||
my $perl_ver = sprintf('%vd', $^V);
|
||||
my @modules = (
|
||||
{
|
||||
package => 'GD',
|
||||
module => 'GD',
|
||||
version => '1.20',
|
||||
feature => [qw(graphical_reports new_charts old_charts)],
|
||||
},
|
||||
{
|
||||
package => 'Chart',
|
||||
module => 'Chart::Lines',
|
||||
# Versions below 2.1 cannot be detected accurately.
|
||||
version => '2.1',
|
||||
feature => [qw(new_charts old_charts)],
|
||||
},
|
||||
{
|
||||
package => 'Template-GD',
|
||||
# This module tells us whether or not Template-GD is installed
|
||||
# on Template-Toolkits after 2.14, and still works with 2.14 and lower.
|
||||
module => 'Template::Plugin::GD::Image',
|
||||
version => 0,
|
||||
feature => ['graphical_reports'],
|
||||
},
|
||||
{
|
||||
package => 'GDTextUtil',
|
||||
module => 'GD::Text',
|
||||
version => 0,
|
||||
feature => ['graphical_reports'],
|
||||
},
|
||||
{
|
||||
package => 'GDGraph',
|
||||
module => 'GD::Graph',
|
||||
version => 0,
|
||||
feature => ['graphical_reports'],
|
||||
},
|
||||
{
|
||||
package => 'MIME-tools',
|
||||
# MIME::Parser is packaged as MIME::Tools on ActiveState Perl
|
||||
module => ON_WINDOWS ? 'MIME::Tools' : 'MIME::Parser',
|
||||
version => '5.406',
|
||||
feature => ['moving'],
|
||||
},
|
||||
{
|
||||
package => 'libwww-perl',
|
||||
module => 'LWP::UserAgent',
|
||||
version => 0,
|
||||
feature => ['updates'],
|
||||
},
|
||||
{
|
||||
package => 'XML-Twig',
|
||||
module => 'XML::Twig',
|
||||
version => 0,
|
||||
feature => ['moving', 'updates'],
|
||||
},
|
||||
{
|
||||
package => 'PatchReader',
|
||||
module => 'PatchReader',
|
||||
version => '0.9.4',
|
||||
feature => ['patch_viewer'],
|
||||
},
|
||||
{
|
||||
package => 'perl-ldap',
|
||||
module => 'Net::LDAP',
|
||||
version => 0,
|
||||
feature => ['auth_ldap'],
|
||||
},
|
||||
{
|
||||
package => 'Authen-SASL',
|
||||
module => 'Authen::SASL',
|
||||
version => 0,
|
||||
feature => ['smtp_auth'],
|
||||
},
|
||||
{
|
||||
package => 'RadiusPerl',
|
||||
module => 'Authen::Radius',
|
||||
version => 0,
|
||||
feature => ['auth_radius'],
|
||||
},
|
||||
{
|
||||
package => 'SOAP-Lite',
|
||||
module => 'SOAP::Lite',
|
||||
# Fixes various bugs, including 542931 and 552353 + stops
|
||||
# throwing warnings with Perl 5.12.
|
||||
version => '0.712',
|
||||
feature => ['xmlrpc'],
|
||||
},
|
||||
{
|
||||
package => 'JSON-RPC',
|
||||
module => 'JSON::RPC',
|
||||
version => 0,
|
||||
feature => ['jsonrpc'],
|
||||
},
|
||||
{
|
||||
package => 'JSON-XS',
|
||||
module => 'JSON::XS',
|
||||
# 2.0 is the first version that will work with JSON::RPC.
|
||||
version => '2.0',
|
||||
feature => ['jsonrpc_faster'],
|
||||
},
|
||||
{
|
||||
package => 'Test-Taint',
|
||||
module => 'Test::Taint',
|
||||
version => 0,
|
||||
feature => ['jsonrpc', 'xmlrpc'],
|
||||
},
|
||||
{
|
||||
# We need the 'utf8_mode' method of HTML::Parser, for HTML::Scrubber.
|
||||
package => 'HTML-Parser',
|
||||
module => 'HTML::Parser',
|
||||
version => (vers_cmp($perl_ver, '5.13.3') > -1) ? '3.67' : '3.40',
|
||||
feature => ['html_desc'],
|
||||
},
|
||||
{
|
||||
package => 'HTML-Scrubber',
|
||||
module => 'HTML::Scrubber',
|
||||
version => 0,
|
||||
feature => ['html_desc'],
|
||||
},
|
||||
|
||||
# Inbound Email
|
||||
{
|
||||
package => 'Email-MIME-Attachment-Stripper',
|
||||
module => 'Email::MIME::Attachment::Stripper',
|
||||
version => 0,
|
||||
feature => ['inbound_email'],
|
||||
},
|
||||
{
|
||||
package => 'Email-Reply',
|
||||
module => 'Email::Reply',
|
||||
version => 0,
|
||||
feature => ['inbound_email'],
|
||||
},
|
||||
|
||||
# Mail Queueing
|
||||
{
|
||||
package => 'TheSchwartz',
|
||||
module => 'TheSchwartz',
|
||||
version => 0,
|
||||
feature => ['jobqueue'],
|
||||
},
|
||||
{
|
||||
package => 'Daemon-Generic',
|
||||
module => 'Daemon::Generic',
|
||||
version => 0,
|
||||
feature => ['jobqueue'],
|
||||
},
|
||||
|
||||
# mod_perl
|
||||
{
|
||||
package => 'mod_perl',
|
||||
module => 'mod_perl2',
|
||||
version => '1.999022',
|
||||
feature => ['mod_perl'],
|
||||
},
|
||||
{
|
||||
package => 'Apache-SizeLimit',
|
||||
module => 'Apache2::SizeLimit',
|
||||
# 0.93 fixes problems on Linux and Windows, and changes the
|
||||
# syntax used by SizeLimit.
|
||||
version => '0.93',
|
||||
feature => ['mod_perl'],
|
||||
},
|
||||
{
|
||||
package => 'Math-Random-Secure',
|
||||
module => 'Math::Random::Secure',
|
||||
# This is the first version that installs properly on Windows.
|
||||
version => '0.05',
|
||||
feature => ['rand_security'],
|
||||
},
|
||||
);
|
||||
|
||||
if (ON_WINDOWS) {
|
||||
# SizeLimit needs Win32::API to work on Windows.
|
||||
push(@modules, {
|
||||
package => 'Win32-API',
|
||||
module => 'Win32::API',
|
||||
version => 0,
|
||||
feature => ['mod_perl'],
|
||||
});
|
||||
}
|
||||
|
||||
my $extra_modules = _get_extension_requirements('OPTIONAL_MODULES');
|
||||
push(@modules, @$extra_modules);
|
||||
return \@modules;
|
||||
};
|
||||
|
||||
# This maps features to the files that require that feature in order
|
||||
# to compile. It is used by t/001compile.t and mod_perl.pl.
|
||||
use constant FEATURE_FILES => (
|
||||
jsonrpc => ['Bugzilla/WebService/Server/JSONRPC.pm', 'jsonrpc.cgi'],
|
||||
xmlrpc => ['Bugzilla/WebService/Server/XMLRPC.pm', 'xmlrpc.cgi',
|
||||
'Bugzilla/WebService.pm', 'Bugzilla/WebService/*.pm'],
|
||||
moving => ['importxml.pl'],
|
||||
auth_ldap => ['Bugzilla/Auth/Verify/LDAP.pm'],
|
||||
auth_radius => ['Bugzilla/Auth/Verify/RADIUS.pm'],
|
||||
inbound_email => ['email_in.pl'],
|
||||
jobqueue => ['Bugzilla/Job/*', 'Bugzilla/JobQueue.pm',
|
||||
'Bugzilla/JobQueue/*', 'jobqueue.pl'],
|
||||
patch_viewer => ['Bugzilla/Attachment/PatchReader.pm'],
|
||||
updates => ['Bugzilla/Update.pm'],
|
||||
);
|
||||
|
||||
# This implements the REQUIRED_MODULES and OPTIONAL_MODULES stuff
|
||||
# described in in Bugzilla::Extension.
|
||||
sub _get_extension_requirements {
|
||||
my ($function) = @_;
|
||||
|
||||
my $packages = extension_requirement_packages();
|
||||
my @modules;
|
||||
foreach my $package (@$packages) {
|
||||
if ($package->can($function)) {
|
||||
my $extra_modules = $package->$function;
|
||||
push(@modules, @$extra_modules);
|
||||
}
|
||||
}
|
||||
return \@modules;
|
||||
};
|
||||
|
||||
sub check_requirements {
|
||||
my ($output) = @_;
|
||||
|
||||
print "\n", install_string('checking_modules'), "\n" if $output;
|
||||
my $root = ROOT_USER;
|
||||
my $missing = _check_missing(REQUIRED_MODULES, $output);
|
||||
|
||||
print "\n", install_string('checking_dbd'), "\n" if $output;
|
||||
my $have_one_dbd = 0;
|
||||
my $db_modules = DB_MODULE;
|
||||
foreach my $db (keys %$db_modules) {
|
||||
my $dbd = $db_modules->{$db}->{dbd};
|
||||
$have_one_dbd = 1 if have_vers($dbd, $output);
|
||||
}
|
||||
|
||||
print "\n", install_string('checking_optional'), "\n" if $output;
|
||||
my $missing_optional = _check_missing(OPTIONAL_MODULES, $output);
|
||||
|
||||
my $missing_apache = _missing_apache_modules(APACHE_MODULES, $output);
|
||||
|
||||
# If we're running on Windows, reset the input line terminator so that
|
||||
# console input works properly - loading CGI tends to mess it up
|
||||
$/ = "\015\012" if ON_WINDOWS;
|
||||
|
||||
my $pass = !scalar(@$missing) && $have_one_dbd;
|
||||
return {
|
||||
pass => $pass,
|
||||
one_dbd => $have_one_dbd,
|
||||
missing => $missing,
|
||||
optional => $missing_optional,
|
||||
apache => $missing_apache,
|
||||
any_missing => !$pass || scalar(@$missing_optional),
|
||||
};
|
||||
}
|
||||
|
||||
# A helper for check_requirements
|
||||
sub _check_missing {
|
||||
my ($modules, $output) = @_;
|
||||
|
||||
my @missing;
|
||||
foreach my $module (@$modules) {
|
||||
unless (have_vers($module, $output)) {
|
||||
push(@missing, $module);
|
||||
}
|
||||
}
|
||||
|
||||
return \@missing;
|
||||
}
|
||||
|
||||
sub _missing_apache_modules {
|
||||
my ($modules, $output) = @_;
|
||||
my $apachectl = _get_apachectl();
|
||||
return [] if !$apachectl;
|
||||
my $command = "$apachectl -t -D DUMP_MODULES";
|
||||
my $cmd_info = `$command 2>&1`;
|
||||
# If apachectl returned a value greater than 0, then there was an
|
||||
# error parsing Apache's configuration, and we can't check modules.
|
||||
my $retval = $?;
|
||||
if ($retval > 0) {
|
||||
print STDERR install_string('apachectl_failed',
|
||||
{ command => $command, root => ROOT_USER }), "\n";
|
||||
return [];
|
||||
}
|
||||
my @missing;
|
||||
foreach my $module (keys %$modules) {
|
||||
my $ok = _check_apache_module($module, $modules->{$module},
|
||||
$cmd_info, $output);
|
||||
push(@missing, $module) if !$ok;
|
||||
}
|
||||
return \@missing;
|
||||
}
|
||||
|
||||
sub _get_apachectl {
|
||||
foreach my $bin_name (APACHE) {
|
||||
my $bin = bin_loc($bin_name);
|
||||
return $bin if $bin;
|
||||
}
|
||||
# Try again with a possibly different path.
|
||||
foreach my $bin_name (APACHE) {
|
||||
my $bin = bin_loc($bin_name, APACHE_PATH);
|
||||
return $bin if $bin;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub _check_apache_module {
|
||||
my ($module, $config_name, $mod_info, $output) = @_;
|
||||
my $ok;
|
||||
if ($mod_info =~ /^\s+\Q$config_name\E\b/m) {
|
||||
$ok = 1;
|
||||
}
|
||||
if ($output) {
|
||||
_checking_for({ package => $module, ok => $ok });
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
|
||||
sub print_module_instructions {
|
||||
my ($check_results, $output) = @_;
|
||||
|
||||
# First we print the long explanatory messages.
|
||||
|
||||
if (scalar @{$check_results->{missing}}) {
|
||||
print install_string('modules_message_required');
|
||||
}
|
||||
|
||||
if (!$check_results->{one_dbd}) {
|
||||
print install_string('modules_message_db');
|
||||
}
|
||||
|
||||
if (my @missing = @{$check_results->{optional}} and $output) {
|
||||
print install_string('modules_message_optional');
|
||||
# Now we have to determine how large the table cols will be.
|
||||
my $longest_name = max(map(length($_->{package}), @missing));
|
||||
|
||||
# The first column header is at least 11 characters long.
|
||||
$longest_name = 11 if $longest_name < 11;
|
||||
|
||||
# The table is TABLE_WIDTH characters long. There are seven mandatory
|
||||
# characters (* and space) in the string. So, we have a total
|
||||
# of TABLE_WIDTH - 7 characters to work with.
|
||||
my $remaining_space = (TABLE_WIDTH - 7) - $longest_name;
|
||||
print '*' x TABLE_WIDTH . "\n";
|
||||
printf "* \%${longest_name}s * %-${remaining_space}s *\n",
|
||||
'MODULE NAME', 'ENABLES FEATURE(S)';
|
||||
print '*' x TABLE_WIDTH . "\n";
|
||||
foreach my $package (@missing) {
|
||||
printf "* \%${longest_name}s * %-${remaining_space}s *\n",
|
||||
$package->{package},
|
||||
_translate_feature($package->{feature});
|
||||
}
|
||||
}
|
||||
|
||||
if (my @missing = @{ $check_results->{apache} }) {
|
||||
print install_string('modules_message_apache');
|
||||
my $missing_string = join(', ', @missing);
|
||||
my $size = TABLE_WIDTH - 7;
|
||||
printf "* \%-${size}s *\n", $missing_string;
|
||||
my $spaces = TABLE_WIDTH - 2;
|
||||
print "*", (' ' x $spaces), "*\n";
|
||||
}
|
||||
|
||||
my $need_module_instructions =
|
||||
( (!$output and @{$check_results->{missing}})
|
||||
or ($output and $check_results->{any_missing}) ) ? 1 : 0;
|
||||
|
||||
if ($need_module_instructions or @{ $check_results->{apache} }) {
|
||||
# If any output was required, we want to close the "table"
|
||||
print "*" x TABLE_WIDTH . "\n";
|
||||
}
|
||||
|
||||
# And now we print the actual installation commands.
|
||||
|
||||
if (my @missing = @{$check_results->{optional}} and $output) {
|
||||
print install_string('commands_optional') . "\n\n";
|
||||
foreach my $module (@missing) {
|
||||
my $command = install_command($module);
|
||||
printf "%15s: $command\n", $module->{package};
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
if (!$check_results->{one_dbd}) {
|
||||
print install_string('commands_dbd') . "\n";
|
||||
my %db_modules = %{DB_MODULE()};
|
||||
foreach my $db (keys %db_modules) {
|
||||
my $command = install_command($db_modules{$db}->{dbd});
|
||||
printf "%10s: \%s\n", $db_modules{$db}->{name}, $command;
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
if (my @missing = @{$check_results->{missing}}) {
|
||||
print colored(install_string('commands_required'), COLOR_ERROR), "\n";
|
||||
foreach my $package (@missing) {
|
||||
my $command = install_command($package);
|
||||
print " $command\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($output && $check_results->{any_missing} && !ON_ACTIVESTATE
|
||||
&& !$check_results->{hide_all})
|
||||
{
|
||||
print install_string('install_all', { perl => $^X });
|
||||
}
|
||||
if (!$check_results->{pass}) {
|
||||
print colored(install_string('installation_failed'), COLOR_ERROR),
|
||||
"\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub _translate_feature {
|
||||
my $features = shift;
|
||||
my @strings;
|
||||
foreach my $feature (@$features) {
|
||||
push(@strings, install_string("feature_$feature"));
|
||||
}
|
||||
return join(', ', @strings);
|
||||
}
|
||||
|
||||
sub check_graphviz {
|
||||
my ($output) = @_;
|
||||
|
||||
return 1 if (Bugzilla->params->{'webdotbase'} =~ /^https?:/);
|
||||
|
||||
my $return;
|
||||
$return = 1 if -x Bugzilla->params->{'webdotbase'};
|
||||
|
||||
if ($output) {
|
||||
_checking_for({ package => 'GraphViz', ok => $return });
|
||||
}
|
||||
|
||||
if (!$return) {
|
||||
print "not a valid executable: " . Bugzilla->params->{'webdotbase'}
|
||||
. "\n";
|
||||
}
|
||||
|
||||
my $webdotdir = bz_locations()->{'webdotdir'};
|
||||
# Check .htaccess allows access to generated images
|
||||
if (-e "$webdotdir/.htaccess") {
|
||||
my $htaccess = new IO::File("$webdotdir/.htaccess", 'r')
|
||||
|| die "$webdotdir/.htaccess: " . $!;
|
||||
if (!grep(/png/, $htaccess->getlines)) {
|
||||
print "Dependency graph images are not accessible.\n";
|
||||
print "delete $webdotdir/.htaccess and re-run checksetup.pl to fix.\n";
|
||||
}
|
||||
$htaccess->close;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
# This was originally clipped from the libnet Makefile.PL, adapted here to
|
||||
# use the below vers_cmp routine for accurate version checking.
|
||||
sub have_vers {
|
||||
my ($params, $output) = @_;
|
||||
my $module = $params->{module};
|
||||
my $package = $params->{package};
|
||||
if (!$package) {
|
||||
$package = $module;
|
||||
$package =~ s/::/-/g;
|
||||
}
|
||||
my $wanted = $params->{version};
|
||||
|
||||
eval "require $module;";
|
||||
# Don't let loading a module change the output-encoding of STDOUT
|
||||
# or STDERR. (CGI.pm tries to set "binmode" on these file handles when
|
||||
# it's loaded, and other modules may do the same in the future.)
|
||||
Bugzilla::Install::Util::set_output_encoding();
|
||||
|
||||
# VERSION is provided by UNIVERSAL::, and can be called even if
|
||||
# the module isn't loaded.
|
||||
my $vnum = $module->VERSION || -1;
|
||||
|
||||
# CGI's versioning scheme went 2.75, 2.751, 2.752, 2.753, 2.76
|
||||
# That breaks the standard version tests, so we need to manually correct
|
||||
# the version
|
||||
if ($module eq 'CGI' && $vnum =~ /(2\.7\d)(\d+)/) {
|
||||
$vnum = $1 . "." . $2;
|
||||
}
|
||||
# CPAN did a similar thing, where it has versions like 1.9304.
|
||||
if ($module eq 'CPAN' and $vnum =~ /^(\d\.\d{2})\d{2}$/) {
|
||||
$vnum = $1;
|
||||
}
|
||||
|
||||
my $vok = (vers_cmp($vnum,$wanted) > -1);
|
||||
my $blacklisted;
|
||||
if ($vok && $params->{blacklist}) {
|
||||
$blacklisted = grep($vnum =~ /$_/, @{$params->{blacklist}});
|
||||
$vok = 0 if $blacklisted;
|
||||
}
|
||||
|
||||
if ($output) {
|
||||
_checking_for({
|
||||
package => $package, ok => $vok, wanted => $wanted,
|
||||
found => $vnum, blacklisted => $blacklisted
|
||||
});
|
||||
}
|
||||
|
||||
return $vok ? 1 : 0;
|
||||
}
|
||||
|
||||
sub _checking_for {
|
||||
my ($params) = @_;
|
||||
my ($package, $ok, $wanted, $blacklisted, $found) =
|
||||
@$params{qw(package ok wanted blacklisted found)};
|
||||
|
||||
my $ok_string = $ok ? install_string('module_ok') : '';
|
||||
|
||||
# If we're actually checking versions (like for Perl modules), then
|
||||
# we have some rather complex logic to determine what we want to
|
||||
# show. If we're not checking versions (like for GraphViz) we just
|
||||
# show "ok" or "not found".
|
||||
if (exists $params->{found}) {
|
||||
my $found_string;
|
||||
# We do a string compare in case it's non-numeric. We make sure
|
||||
# it's not a version object as negative versions are forbidden.
|
||||
if ($found && !ref($found) && $found eq '-1') {
|
||||
$found_string = install_string('module_not_found');
|
||||
}
|
||||
elsif ($found) {
|
||||
$found_string = install_string('module_found', { ver => $found });
|
||||
}
|
||||
else {
|
||||
$found_string = install_string('module_unknown_version');
|
||||
}
|
||||
$ok_string = $ok ? "$ok_string: $found_string" : $found_string;
|
||||
}
|
||||
elsif (!$ok) {
|
||||
$ok_string = install_string('module_not_found');
|
||||
}
|
||||
|
||||
my $black_string = $blacklisted ? install_string('blacklisted') : '';
|
||||
my $want_string = $wanted ? "v$wanted" : install_string('any');
|
||||
|
||||
my $str = sprintf "%s %20s %-11s $ok_string $black_string\n",
|
||||
install_string('checking_for'), $package, "($want_string)";
|
||||
print $ok ? $str : colored($str, COLOR_ERROR);
|
||||
}
|
||||
|
||||
sub install_command {
|
||||
my $module = shift;
|
||||
my ($command, $package);
|
||||
|
||||
if (ON_ACTIVESTATE) {
|
||||
$command = 'ppm install %s';
|
||||
$package = $module->{package};
|
||||
}
|
||||
else {
|
||||
$command = "$^X install-module.pl \%s";
|
||||
# Non-Windows installations need to use module names, because
|
||||
# CPAN doesn't understand package names.
|
||||
$package = $module->{module};
|
||||
}
|
||||
return sprintf $command, $package;
|
||||
}
|
||||
|
||||
# This does a reverse mapping for FEATURE_FILES.
|
||||
sub map_files_to_features {
|
||||
my %features = FEATURE_FILES;
|
||||
my %files;
|
||||
foreach my $feature (keys %features) {
|
||||
my @my_files = @{ $features{$feature} };
|
||||
foreach my $pattern (@my_files) {
|
||||
foreach my $file (glob $pattern) {
|
||||
$files{$file} = $feature;
|
||||
}
|
||||
}
|
||||
}
|
||||
return \%files;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Install::Requirements - Functions and variables dealing
|
||||
with Bugzilla's perl-module requirements.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is used primarily by C<checksetup.pl> to determine whether
|
||||
or not all of Bugzilla's prerequisites are installed. (That is, all the
|
||||
perl modules it requires.)
|
||||
|
||||
=head1 CONSTANTS
|
||||
|
||||
=over
|
||||
|
||||
=item C<REQUIRED_MODULES>
|
||||
|
||||
An arrayref of hashrefs that describes the perl modules required by
|
||||
Bugzilla. The hashes have three keys:
|
||||
|
||||
=over
|
||||
|
||||
=item C<package> - The name of the Perl package that you'd find on
|
||||
CPAN for this requirement.
|
||||
|
||||
=item C<module> - The name of a module that can be passed to the
|
||||
C<install> command in C<CPAN.pm> to install this module.
|
||||
|
||||
=item C<version> - The version of this module that we require, or C<0>
|
||||
if any version is acceptable.
|
||||
|
||||
=back
|
||||
|
||||
=item C<OPTIONAL_MODULES>
|
||||
|
||||
An arrayref of hashrefs that describes the perl modules that add
|
||||
additional features to Bugzilla if installed. Its hashes have all
|
||||
the fields of L</REQUIRED_MODULES>, plus a C<feature> item--an arrayref
|
||||
of strings that describe what features require this module.
|
||||
|
||||
=item C<FEATURE_FILES>
|
||||
|
||||
A hashref that describes what files should only be compiled if a certain
|
||||
feature is enabled. The feature is the key, and the values are arrayrefs
|
||||
of file names (which are passed to C<glob>, so shell patterns work).
|
||||
|
||||
=back
|
||||
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<check_requirements>
|
||||
|
||||
=over
|
||||
|
||||
=item B<Description>
|
||||
|
||||
This checks what optional or required perl modules are installed, like
|
||||
C<checksetup.pl> does.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
||||
=item C<$output> - C<true> if you want the function to print out information
|
||||
about what it's doing, and the versions of everything installed.
|
||||
|
||||
=back
|
||||
|
||||
=item B<Returns>
|
||||
|
||||
A hashref containing these values:
|
||||
|
||||
=over
|
||||
|
||||
=item C<pass> - Whether or not we have all the mandatory requirements.
|
||||
|
||||
=item C<missing> - An arrayref containing any required modules that
|
||||
are not installed or that are not up-to-date. Each item in the array is
|
||||
a hashref in the format of items from L</REQUIRED_MODULES>.
|
||||
|
||||
=item C<optional> - The same as C<missing>, but for optional modules.
|
||||
|
||||
=item C<apache> - The name of each optional Apache module that is missing.
|
||||
|
||||
=item C<have_one_dbd> - True if at least one C<DBD::> module is installed.
|
||||
|
||||
=item C<any_missing> - True if there are any missing Perl modules, even
|
||||
optional modules.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=item C<check_graphviz($output)>
|
||||
|
||||
Description: Checks if the graphviz binary specified in the
|
||||
C<webdotbase> parameter is a valid binary, or a valid URL.
|
||||
|
||||
Params: C<$output> - C<$true> if you want the function to
|
||||
print out information about what it's doing.
|
||||
|
||||
Returns: C<1> if the check was successful, C<0> otherwise.
|
||||
|
||||
=item C<have_vers($module, $output)>
|
||||
|
||||
Description: Tells you whether or not you have the appropriate
|
||||
version of the module requested. It also prints
|
||||
out a message to the user explaining the check
|
||||
and the result.
|
||||
|
||||
Params: C<$module> - A hashref, in the format of an item from
|
||||
L</REQUIRED_MODULES>.
|
||||
C<$output> - Set to true if you want this function to
|
||||
print information to STDOUT about what it's
|
||||
doing.
|
||||
|
||||
Returns: C<1> if you have the module installed and you have the
|
||||
appropriate version. C<0> otherwise.
|
||||
|
||||
=item C<install_command($module)>
|
||||
|
||||
Description: Prints out the appropriate command to install the
|
||||
module specified, depending on whether you're
|
||||
on Windows or Linux.
|
||||
|
||||
Params: C<$module> - A hashref, in the format of an item from
|
||||
L</REQUIRED_MODULES>.
|
||||
|
||||
Returns: nothing
|
||||
|
||||
=item C<map_files_to_features>
|
||||
|
||||
Returns a hashref where file names are the keys and the value is the feature
|
||||
that must be enabled in order to compile that file.
|
||||
|
||||
=back
|
||||
@@ -1,892 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# 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;
|
||||
|
||||
use Encode;
|
||||
use ExtUtils::MM ();
|
||||
use File::Basename;
|
||||
use File::Spec;
|
||||
use POSIX qw(setlocale LC_CTYPE);
|
||||
use Safe;
|
||||
use Scalar::Util qw(tainted);
|
||||
use Term::ANSIColor qw(colored);
|
||||
use PerlIO;
|
||||
|
||||
use base qw(Exporter);
|
||||
our @EXPORT_OK = qw(
|
||||
bin_loc
|
||||
get_version_and_os
|
||||
extension_code_files
|
||||
extension_package_directory
|
||||
extension_requirement_packages
|
||||
extension_template_directory
|
||||
indicate_progress
|
||||
install_string
|
||||
include_languages
|
||||
template_include_path
|
||||
vers_cmp
|
||||
init_console
|
||||
);
|
||||
|
||||
sub bin_loc {
|
||||
my ($bin, $path) = @_;
|
||||
my @path = $path ? @$path : File::Spec->path;
|
||||
|
||||
foreach my $dir (@path) {
|
||||
next if !-d $dir;
|
||||
my $full_path = File::Spec->catfile($dir, $bin);
|
||||
# MM is an alias for ExtUtils::MM. maybe_command is nice
|
||||
# because it checks .com, .bat, .exe (etc.) on Windows.
|
||||
my $command = MM->maybe_command($full_path);
|
||||
return $command if $command;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
sub get_version_and_os {
|
||||
# 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];
|
||||
if (ON_WINDOWS) {
|
||||
require Win32;
|
||||
$os_name = Win32::GetOSName();
|
||||
}
|
||||
# $os_details[3] is the minor version.
|
||||
return { bz_ver => BUGZILLA_VERSION,
|
||||
perl_ver => sprintf('%vd', $^V),
|
||||
os_name => $os_name,
|
||||
os_ver => $os_details[3] };
|
||||
}
|
||||
|
||||
sub _extension_paths {
|
||||
my $dir = bz_locations()->{'extensionsdir'};
|
||||
my @extension_items = glob("$dir/*");
|
||||
my @paths;
|
||||
foreach my $item (@extension_items) {
|
||||
my $basename = basename($item);
|
||||
# Skip CVS directories and any hidden files/dirs.
|
||||
next if ($basename eq 'CVS' or $basename =~ /^\./);
|
||||
if (-d $item) {
|
||||
if (!-e "$item/disabled") {
|
||||
push(@paths, $item);
|
||||
}
|
||||
}
|
||||
elsif ($item =~ /\.pm$/i) {
|
||||
push(@paths, $item);
|
||||
}
|
||||
}
|
||||
return @paths;
|
||||
}
|
||||
|
||||
sub extension_code_files {
|
||||
my ($requirements_only) = @_;
|
||||
my @files;
|
||||
foreach my $path (_extension_paths()) {
|
||||
my @load_files;
|
||||
if (-d $path) {
|
||||
my $extension_file = "$path/Extension.pm";
|
||||
my $config_file = "$path/Config.pm";
|
||||
if (-e $extension_file) {
|
||||
push(@load_files, $extension_file);
|
||||
}
|
||||
if (-e $config_file) {
|
||||
push(@load_files, $config_file);
|
||||
}
|
||||
|
||||
# Don't load Extension.pm if we just want Config.pm and
|
||||
# we found both.
|
||||
if ($requirements_only and scalar(@load_files) == 2) {
|
||||
shift(@load_files);
|
||||
}
|
||||
}
|
||||
else {
|
||||
push(@load_files, $path);
|
||||
}
|
||||
next if !scalar(@load_files);
|
||||
# We know that these paths are safe, because they came from
|
||||
# extensionsdir and we checked them specifically for their format.
|
||||
# Also, the only thing we ever do with them is pass them to "require".
|
||||
trick_taint($_) foreach @load_files;
|
||||
push(@files, \@load_files);
|
||||
}
|
||||
|
||||
my @additional;
|
||||
my $datadir = bz_locations()->{'datadir'};
|
||||
my $addl_file = "$datadir/extensions/additional";
|
||||
if (-e $addl_file) {
|
||||
open(my $fh, '<', $addl_file) || die "$addl_file: $!";
|
||||
@additional = map { trim($_) } <$fh>;
|
||||
close($fh);
|
||||
}
|
||||
return (\@files, \@additional);
|
||||
}
|
||||
|
||||
# Used by _get_extension_requirements in Bugzilla::Install::Requirements.
|
||||
sub extension_requirement_packages {
|
||||
# If we're in a .cgi script or some time that's not the requirements phase,
|
||||
# just use Bugzilla->extensions. This avoids running the below code during
|
||||
# a normal Bugzilla page, which is important because the below code
|
||||
# doesn't actually function right if it runs after
|
||||
# Bugzilla::Extension->load_all (because stuff has already been loaded).
|
||||
# (This matters because almost every page calls Bugzilla->feature, which
|
||||
# calls OPTIONAL_MODULES, which calls this method.)
|
||||
#
|
||||
# We check if Bugzilla.pm is already loaded, instead of doing a "require",
|
||||
# because we *do* want the code lower down to run during the Requirements
|
||||
# phase of checksetup.pl, instead of Bugzilla->extensions, and Bugzilla.pm
|
||||
# actually *can* be loaded during the Requirements phase if all the
|
||||
# requirements have already been installed.
|
||||
if ($INC{'Bugzilla.pm'}) {
|
||||
return Bugzilla->extensions;
|
||||
}
|
||||
my $packages = _cache()->{extension_requirement_packages};
|
||||
return $packages if $packages;
|
||||
$packages = [];
|
||||
my %package_map;
|
||||
|
||||
my ($file_sets, $extra_packages) = extension_code_files('requirements only');
|
||||
foreach my $file_set (@$file_sets) {
|
||||
my $file = shift @$file_set;
|
||||
my $name = require $file;
|
||||
if ($name =~ /^\d+$/) {
|
||||
die install_string('extension_must_return_name',
|
||||
{ file => $file, returned => $name });
|
||||
}
|
||||
my $package = "Bugzilla::Extension::$name";
|
||||
if ($package->can('package_dir')) {
|
||||
$package->package_dir($file);
|
||||
}
|
||||
else {
|
||||
extension_package_directory($package, $file);
|
||||
}
|
||||
$package_map{$file} = $package;
|
||||
push(@$packages, $package);
|
||||
}
|
||||
foreach my $package (@$extra_packages) {
|
||||
eval("require $package") || die $@;
|
||||
push(@$packages, $package);
|
||||
}
|
||||
|
||||
_cache()->{extension_requirement_packages} = $packages;
|
||||
# Used by Bugzilla::Extension->load if it's called after this method
|
||||
# (which only happens during checksetup.pl, currently).
|
||||
_cache()->{extension_requirement_package_map} = \%package_map;
|
||||
return $packages;
|
||||
}
|
||||
|
||||
# Used in this file and in Bugzilla::Extension.
|
||||
sub extension_template_directory {
|
||||
my $extension = shift;
|
||||
my $class = ref($extension) || $extension;
|
||||
my $base_dir = extension_package_directory($class);
|
||||
if ($base_dir eq bz_locations->{'extensionsdir'}) {
|
||||
return bz_locations->{'templatedir'};
|
||||
}
|
||||
return "$base_dir/template";
|
||||
}
|
||||
|
||||
# For extensions that are in the extensions/ dir, this both sets and fetches
|
||||
# the name of the directory that stores an extension's "stuff". We need this
|
||||
# when determining the template directory for extensions (or other things
|
||||
# that are relative to the extension's base directory).
|
||||
sub extension_package_directory {
|
||||
my ($invocant, $file) = @_;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
|
||||
# $file is set on the first invocation, store the value in the extension's
|
||||
# package for retrieval on subsequent calls
|
||||
my $var;
|
||||
{
|
||||
no warnings 'once';
|
||||
no strict 'refs';
|
||||
$var = \${"${class}::EXTENSION_PACKAGE_DIR"};
|
||||
}
|
||||
if ($file) {
|
||||
$$var = dirname($file);
|
||||
}
|
||||
my $value = $$var;
|
||||
|
||||
# This is for extensions loaded from data/extensions/additional.
|
||||
if (!$value) {
|
||||
my $short_path = $class;
|
||||
$short_path =~ s/::/\//g;
|
||||
$short_path .= ".pm";
|
||||
my $long_path = $INC{$short_path};
|
||||
die "$short_path is not in \%INC" if !$long_path;
|
||||
$value = $long_path;
|
||||
$value =~ s/\.pm//;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
sub indicate_progress {
|
||||
my ($params) = @_;
|
||||
my $current = $params->{current};
|
||||
my $total = $params->{total};
|
||||
my $every = $params->{every} || 1;
|
||||
|
||||
print "." if !($current % $every);
|
||||
if ($current == $total || $current % ($every * 60) == 0) {
|
||||
print "$current/$total (" . int($current * 100 / $total) . "%)\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub install_string {
|
||||
my ($string_id, $vars) = @_;
|
||||
_cache()->{install_string_path} ||= template_include_path();
|
||||
my $path = _cache()->{install_string_path};
|
||||
|
||||
my $string_template;
|
||||
# Find the first template that defines this string.
|
||||
foreach my $dir (@$path) {
|
||||
my $base = "$dir/setup/strings";
|
||||
$string_template = _get_string_from_file($string_id, "$base.txt.pl")
|
||||
if !defined $string_template;
|
||||
last if defined $string_template;
|
||||
}
|
||||
|
||||
die "No language defines the string '$string_id'"
|
||||
if !defined $string_template;
|
||||
|
||||
utf8::decode($string_template) if !utf8::is_utf8($string_template);
|
||||
|
||||
$vars ||= {};
|
||||
my @replace_keys = keys %$vars;
|
||||
foreach my $key (@replace_keys) {
|
||||
my $replacement = $vars->{$key};
|
||||
die "'$key' in '$string_id' is tainted: '$replacement'"
|
||||
if tainted($replacement);
|
||||
# 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.
|
||||
if (grep($replacement =~ /##$key##/, @replace_keys)) {
|
||||
die "Unsafe replacement for '$key' in '$string_id': '$replacement'";
|
||||
}
|
||||
$string_template =~ s/\Q##$key##\E/$replacement/g;
|
||||
}
|
||||
|
||||
return $string_template;
|
||||
}
|
||||
|
||||
sub _wanted_languages {
|
||||
my ($requested, @wanted);
|
||||
|
||||
# Checking SERVER_SOFTWARE is the same as i_am_cgi() in Bugzilla::Util.
|
||||
if (exists $ENV{'SERVER_SOFTWARE'}) {
|
||||
my $cgi = Bugzilla->cgi;
|
||||
$requested = $cgi->http('Accept-Language') || '';
|
||||
my $lang = $cgi->cookie('LANG');
|
||||
push(@wanted, $lang) if $lang;
|
||||
}
|
||||
else {
|
||||
$requested = get_console_locale();
|
||||
}
|
||||
|
||||
push(@wanted, _sort_accept_language($requested));
|
||||
return \@wanted;
|
||||
}
|
||||
|
||||
sub _wanted_to_actual_languages {
|
||||
my ($wanted, $supported) = @_;
|
||||
|
||||
my @actual;
|
||||
foreach my $lang (@$wanted) {
|
||||
# If we support the language we want, or *any version* of
|
||||
# the language we want, it gets pushed into @actual.
|
||||
#
|
||||
# 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)
|
||||
my @found = grep(/^\Q$lang\E(-.+)?$/i, @$supported);
|
||||
push(@actual, @found) if @found;
|
||||
}
|
||||
|
||||
# We always include English at the bottom if it's not there, even if
|
||||
# it wasn't selected by the user.
|
||||
if (!grep($_ eq 'en', @actual)) {
|
||||
push(@actual, 'en');
|
||||
}
|
||||
|
||||
return \@actual;
|
||||
}
|
||||
|
||||
sub supported_languages {
|
||||
my $cache = _cache();
|
||||
return $cache->{supported_languages} if $cache->{supported_languages};
|
||||
|
||||
my @dirs = glob(bz_locations()->{'templatedir'} . "/*");
|
||||
my @languages;
|
||||
foreach my $dir (@dirs) {
|
||||
# It's a language directory only if it contains "default" or
|
||||
# "custom". This auto-excludes CVS directories as well.
|
||||
next if (!-d "$dir/default" and !-d "$dir/custom");
|
||||
my $lang = basename($dir);
|
||||
# Check for language tag format conforming to RFC 1766.
|
||||
next unless $lang =~ /^[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?$/;
|
||||
push(@languages, $lang);
|
||||
}
|
||||
|
||||
$cache->{supported_languages} = \@languages;
|
||||
return \@languages;
|
||||
}
|
||||
|
||||
sub include_languages {
|
||||
my ($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.
|
||||
my $wanted;
|
||||
if ($params->{language}) {
|
||||
# We can pass several languages at once as an arrayref
|
||||
# or a single language.
|
||||
$wanted = $params->{language};
|
||||
$wanted = [$wanted] unless ref $wanted;
|
||||
}
|
||||
else {
|
||||
$wanted = _wanted_languages();
|
||||
}
|
||||
my $supported = supported_languages();
|
||||
my $actual = _wanted_to_actual_languages($wanted, $supported);
|
||||
return @$actual;
|
||||
}
|
||||
|
||||
# Used by template_include_path
|
||||
sub _template_lang_directories {
|
||||
my ($languages, $templatedir) = @_;
|
||||
|
||||
my @add = qw(custom default);
|
||||
my $project = bz_locations->{'project'};
|
||||
unshift(@add, $project) if $project;
|
||||
|
||||
my @result;
|
||||
foreach my $lang (@$languages) {
|
||||
foreach my $dir (@add) {
|
||||
my $full_dir = "$templatedir/$lang/$dir";
|
||||
if (-d $full_dir) {
|
||||
trick_taint($full_dir);
|
||||
push(@result, $full_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
return @result;
|
||||
}
|
||||
|
||||
# Used by template_include_path.
|
||||
sub _template_base_directories {
|
||||
# First, we add extension template directories, because extension templates
|
||||
# override standard templates. Extensions may be localized in the same way
|
||||
# that Bugzilla templates are localized.
|
||||
#
|
||||
# We use extension_requirement_packages instead of Bugzilla->extensions
|
||||
# because this fucntion is called during the requirements phase of
|
||||
# installation (so Bugzilla->extensions isn't available).
|
||||
my $extensions = extension_requirement_packages();
|
||||
my @template_dirs;
|
||||
foreach my $extension (@$extensions) {
|
||||
my $dir;
|
||||
# If there's a template_dir method available in the extension
|
||||
# package, then call it. Note that this has to be defined in
|
||||
# Config.pm for extensions that have a Config.pm, to be effective
|
||||
# during the Requirements phase of checksetup.pl.
|
||||
if ($extension->can('template_dir')) {
|
||||
$dir = $extension->template_dir;
|
||||
}
|
||||
else {
|
||||
$dir = extension_template_directory($extension);
|
||||
}
|
||||
if (-d $dir) {
|
||||
push(@template_dirs, $dir);
|
||||
}
|
||||
}
|
||||
|
||||
# Extensions may also contain *only* templates, in which case they
|
||||
# won't show up in extension_requirement_packages.
|
||||
foreach my $path (_extension_paths()) {
|
||||
next if !-d $path;
|
||||
if (!-e "$path/Extension.pm" and !-e "$path/Config.pm"
|
||||
and -d "$path/template")
|
||||
{
|
||||
push(@template_dirs, "$path/template");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
push(@template_dirs, bz_locations()->{'templatedir'});
|
||||
return \@template_dirs;
|
||||
}
|
||||
|
||||
sub template_include_path {
|
||||
my ($params) = @_;
|
||||
my @used_languages = include_languages($params);
|
||||
# Now, we add template directories in the order they will be searched:
|
||||
my $template_dirs = _template_base_directories();
|
||||
|
||||
my @include_path;
|
||||
foreach my $template_dir (@$template_dirs) {
|
||||
my @lang_dirs = _template_lang_directories(\@used_languages,
|
||||
$template_dir);
|
||||
# Hooks get each set of extension directories separately.
|
||||
if ($params->{hook}) {
|
||||
push(@include_path, \@lang_dirs);
|
||||
}
|
||||
# Whereas everything else just gets a whole INCLUDE_PATH.
|
||||
else {
|
||||
push(@include_path, @lang_dirs);
|
||||
}
|
||||
}
|
||||
return \@include_path;
|
||||
}
|
||||
|
||||
# This is taken straight from Sort::Versions 1.5, which is not included
|
||||
# with perl by default.
|
||||
sub vers_cmp {
|
||||
my ($a, $b) = @_;
|
||||
|
||||
# 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);
|
||||
while (@A and @B) {
|
||||
$A = shift @A;
|
||||
$B = shift @B;
|
||||
if ($A eq '-' and $B eq '-') {
|
||||
next;
|
||||
} elsif ( $A eq '-' ) {
|
||||
return -1;
|
||||
} elsif ( $B eq '-') {
|
||||
return 1;
|
||||
} elsif ($A eq '.' and $B eq '.') {
|
||||
next;
|
||||
} elsif ( $A eq '.' ) {
|
||||
return -1;
|
||||
} elsif ( $B eq '.' ) {
|
||||
return 1;
|
||||
} elsif ($A =~ /^\d+$/ and $B =~ /^\d+$/) {
|
||||
if ($A =~ /^0/ || $B =~ /^0/) {
|
||||
return $A cmp $B if $A cmp $B;
|
||||
} else {
|
||||
return $A <=> $B if $A <=> $B;
|
||||
}
|
||||
} else {
|
||||
$A = uc $A;
|
||||
$B = uc $B;
|
||||
return $A cmp $B if $A cmp $B;
|
||||
}
|
||||
}
|
||||
@A <=> @B;
|
||||
}
|
||||
|
||||
######################
|
||||
# Helper Subroutines #
|
||||
######################
|
||||
|
||||
# Used by install_string
|
||||
sub _get_string_from_file {
|
||||
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.
|
||||
sub _sort_accept_language {
|
||||
sub sortQvalue { $b->{'qvalue'} <=> $a->{'qvalue'} }
|
||||
my $accept_language = $_[0];
|
||||
|
||||
# clean up string.
|
||||
$accept_language =~ s/[^A-Za-z;q=0-9\.\-,]//g;
|
||||
my @qlanguages;
|
||||
my @languages;
|
||||
foreach(split /,/, $accept_language) {
|
||||
if (m/([A-Za-z\-]+)(?:;q=(\d(?:\.\d+)))?/) {
|
||||
my $lang = $1;
|
||||
my $qvalue = $2;
|
||||
$qvalue = 1 if not defined $qvalue;
|
||||
next if $qvalue == 0;
|
||||
$qvalue = 1 if $qvalue > 1;
|
||||
push(@qlanguages, {'qvalue' => $qvalue, 'language' => $lang});
|
||||
}
|
||||
}
|
||||
|
||||
return map($_->{'language'}, (sort sortQvalue @qlanguages));
|
||||
}
|
||||
|
||||
sub get_console_locale {
|
||||
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.
|
||||
if ($locale =~ /^([^\.]+)/) {
|
||||
$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.
|
||||
if ($locale =~ /^(\S+)\-/) {
|
||||
$language = $1;
|
||||
$locale .= ",$language";
|
||||
}
|
||||
else {
|
||||
$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.
|
||||
if ($language = Locale::Language::language2code($language)) {
|
||||
$locale .= ",$language";
|
||||
}
|
||||
|
||||
return $locale;
|
||||
}
|
||||
|
||||
sub set_output_encoding {
|
||||
# If we've already set an encoding layer on STDOUT, don't
|
||||
# add another one.
|
||||
my @stdout_layers = PerlIO::get_layers(STDOUT);
|
||||
return if grep(/^encoding/, @stdout_layers);
|
||||
|
||||
my $encoding;
|
||||
if (ON_WINDOWS and eval { require Win32::Console }) {
|
||||
# Although setlocale() works on Windows, it doesn't always return
|
||||
# the current *console's* encoding. So we use OutputCP here instead,
|
||||
# when we can.
|
||||
$encoding = Win32::Console::OutputCP();
|
||||
}
|
||||
else {
|
||||
my $locale = setlocale(LC_CTYPE);
|
||||
if ($locale =~ /\.([^\.]+)$/) {
|
||||
$encoding = $1;
|
||||
}
|
||||
}
|
||||
$encoding = "cp$encoding" if ON_WINDOWS;
|
||||
|
||||
$encoding = Encode::resolve_alias($encoding) if $encoding;
|
||||
if ($encoding and $encoding !~ /utf-8/i) {
|
||||
binmode STDOUT, ":encoding($encoding)";
|
||||
binmode STDERR, ":encoding($encoding)";
|
||||
}
|
||||
else {
|
||||
binmode STDOUT, ':utf8';
|
||||
binmode STDERR, ':utf8';
|
||||
}
|
||||
}
|
||||
|
||||
sub init_console {
|
||||
eval { ON_WINDOWS && require Win32::Console::ANSI; };
|
||||
$ENV{'ANSI_COLORS_DISABLED'} = 1 if ($@ || !-t *STDOUT);
|
||||
$SIG{__DIE__} = \&_console_die;
|
||||
prevent_windows_dialog_boxes();
|
||||
set_output_encoding();
|
||||
}
|
||||
|
||||
sub _console_die {
|
||||
my ($message) = @_;
|
||||
# $^S means "we are in an eval"
|
||||
if ($^S) {
|
||||
die $message;
|
||||
}
|
||||
# Remove newlines from the message before we color it, and then
|
||||
# add them back in on display. Otherwise the ANSI escape code
|
||||
# for resetting the color comes after the newline, and Perl thinks
|
||||
# that it should put "at Bugzilla/Install.pm line 1234" after the
|
||||
# message.
|
||||
$message =~ s/\n+$//;
|
||||
# We put quotes around the message to stringify any object exceptions,
|
||||
# like Template::Exception.
|
||||
die colored("$message", COLOR_ERROR) . "\n";
|
||||
}
|
||||
|
||||
sub prevent_windows_dialog_boxes {
|
||||
# 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).
|
||||
#
|
||||
# Win32::API ships with ActiveState by default, though there could
|
||||
# theoretically be a Windows installation without it, I suppose.
|
||||
if (ON_WINDOWS and eval { require Win32::API }) {
|
||||
# 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."
|
||||
my $SetErrorMode = Win32::API->new('kernel32', 'SetErrorMode',
|
||||
'I', 'I');
|
||||
my $SEM_FAILCRITICALERRORS = 0x0001;
|
||||
my $SEM_NOGPFAULTERRORBOX = 0x0002;
|
||||
$SetErrorMode->Call($SEM_FAILCRITICALERRORS | $SEM_NOGPFAULTERRORBOX);
|
||||
}
|
||||
}
|
||||
|
||||
# This is like request_cache, but it's used only by installation code
|
||||
# for checksetup.pl and things like that.
|
||||
our $_cache = {};
|
||||
sub _cache {
|
||||
# If the normal request_cache is available (which happens any time
|
||||
# after the requirements phase) then we should use that.
|
||||
if (eval { Bugzilla->request_cache; }) {
|
||||
return Bugzilla->request_cache;
|
||||
}
|
||||
return $_cache;
|
||||
}
|
||||
|
||||
###############################
|
||||
# Copied from Bugzilla::Util #
|
||||
##############################
|
||||
|
||||
sub trick_taint {
|
||||
require Carp;
|
||||
Carp::confess("Undef to trick_taint") unless defined $_[0];
|
||||
my $match = $_[0] =~ /^(.*)$/s;
|
||||
$_[0] = $match ? $1 : undef;
|
||||
return (defined($_[0]));
|
||||
}
|
||||
|
||||
sub trim {
|
||||
my ($str) = @_;
|
||||
if ($str) {
|
||||
$str =~ s/^\s+//g;
|
||||
$str =~ s/\s+$//g;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
__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.
|
||||
|
||||
=item C<init_console>
|
||||
|
||||
Sets the C<ANSI_COLORS_DISABLED> and C<HTTP_ACCEPT_LANGUAGE> environment variables.
|
||||
|
||||
=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>
|
||||
|
||||
Used by L<Bugzilla::Template> to determine the languages' list which
|
||||
are compiled with the browser's I<Accept-Language> and the languages
|
||||
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
|
||||
@@ -1,57 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2008
|
||||
# Mozilla Corporation. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Mark Smith <mark@mozilla.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Job::Mailer;
|
||||
use strict;
|
||||
use Bugzilla::Mailer;
|
||||
BEGIN { eval "use base qw(TheSchwartz::Worker)"; }
|
||||
|
||||
# The longest we expect a job to possibly take, in seconds.
|
||||
use constant grab_for => 300;
|
||||
# We don't want email to fail permanently very easily. Retry for 30 days.
|
||||
use constant max_retries => 725;
|
||||
|
||||
# The first few retries happen quickly, but after that we wait an hour for
|
||||
# each retry.
|
||||
sub retry_delay {
|
||||
my ($class, $num_retries) = @_;
|
||||
if ($num_retries < 5) {
|
||||
return (10, 30, 60, 300, 600)[$num_retries];
|
||||
}
|
||||
# One hour
|
||||
return 60*60;
|
||||
}
|
||||
|
||||
sub work {
|
||||
my ($class, $job) = @_;
|
||||
my $msg = $job->arg->{msg};
|
||||
my $success = eval { MessageToMTA($msg, 1); 1; };
|
||||
if (!$success) {
|
||||
$job->failed($@);
|
||||
undef $@;
|
||||
}
|
||||
else {
|
||||
$job->completed;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,110 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2008
|
||||
# Mozilla Corporation. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Mark Smith <mark@mozilla.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::JobQueue;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Install::Util qw(install_string);
|
||||
use base qw(TheSchwartz);
|
||||
|
||||
# This maps job names for Bugzilla::JobQueue to the appropriate modules.
|
||||
# If you add new types of jobs, you should add a mapping here.
|
||||
use constant JOB_MAP => {
|
||||
send_mail => 'Bugzilla::Job::Mailer',
|
||||
};
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
|
||||
if (!Bugzilla->feature('jobqueue')) {
|
||||
ThrowCodeError('feature_disabled', { feature => 'jobqueue' });
|
||||
}
|
||||
|
||||
my $lc = Bugzilla->localconfig;
|
||||
# We need to use the main DB as TheSchwartz module is going
|
||||
# to write to it.
|
||||
my $self = $class->SUPER::new(
|
||||
databases => [{
|
||||
dsn => Bugzilla->dbh_main->{private_bz_dsn},
|
||||
user => $lc->{db_user},
|
||||
pass => $lc->{db_pass},
|
||||
prefix => 'ts_',
|
||||
}],
|
||||
);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
# A way to get access to the underlying databases directly.
|
||||
sub bz_databases {
|
||||
my $self = shift;
|
||||
my @hashes = keys %{ $self->{databases} };
|
||||
return map { $self->driver_for($_) } @hashes;
|
||||
}
|
||||
|
||||
# inserts a job into the queue to be processed and returns immediately
|
||||
sub insert {
|
||||
my $self = shift;
|
||||
my $job = shift;
|
||||
|
||||
my $mapped_job = JOB_MAP->{$job};
|
||||
ThrowCodeError('jobqueue_no_job_mapping', { job => $job })
|
||||
if !$mapped_job;
|
||||
unshift(@_, $mapped_job);
|
||||
|
||||
my $retval = $self->SUPER::insert(@_);
|
||||
# XXX Need to get an error message here if insert fails, but
|
||||
# I don't see any way to do that in TheSchwartz.
|
||||
ThrowCodeError('jobqueue_insert_failed', { job => $job, errmsg => $@ })
|
||||
if !$retval;
|
||||
|
||||
return $retval;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::JobQueue - Interface between Bugzilla and TheSchwartz.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla;
|
||||
|
||||
my $obj = Bugzilla->job_queue();
|
||||
$obj->insert('send_mail', { msg => $message });
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Certain tasks should be done asyncronously. The job queue system allows
|
||||
Bugzilla to use some sort of service to schedule jobs to happen asyncronously.
|
||||
|
||||
=head2 Inserting a Job
|
||||
|
||||
See the synopsis above for an easy to follow example on how to insert a
|
||||
job into the queue. Give it a name and some arguments and the job will
|
||||
be sent away to be done later.
|
||||
@@ -1,228 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2008
|
||||
# Mozilla Corporation. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Mark Smith <mark@mozilla.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
# XXX In order to support Windows, we have to make gd_redirect_output
|
||||
# use Log4Perl or something instead of calling "logger". We probably
|
||||
# also need to use Win32::Daemon or something like that to daemonize.
|
||||
|
||||
package Bugzilla::JobQueue::Runner;
|
||||
|
||||
use strict;
|
||||
use Cwd qw(abs_path);
|
||||
use File::Basename;
|
||||
use File::Copy;
|
||||
use Pod::Usage;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::JobQueue;
|
||||
use Bugzilla::Util qw(get_text);
|
||||
BEGIN { eval "use base qw(Daemon::Generic)"; }
|
||||
|
||||
our $VERSION = BUGZILLA_VERSION;
|
||||
|
||||
# Info we need to install/uninstall the daemon.
|
||||
our $chkconfig = "/sbin/chkconfig";
|
||||
our $initd = "/etc/init.d";
|
||||
our $initscript = "bugzilla-queue";
|
||||
|
||||
# The Daemon::Generic docs say that it uses all sorts of
|
||||
# things from gd_preconfig, but in fact it does not. The
|
||||
# only thing it uses from gd_preconfig is the "pidfile"
|
||||
# config parameter.
|
||||
sub gd_preconfig {
|
||||
my $self = shift;
|
||||
|
||||
my $pidfile = $self->{gd_args}{pidfile};
|
||||
if (!$pidfile) {
|
||||
$pidfile = bz_locations()->{datadir} . '/' . $self->{gd_progname}
|
||||
. ".pid";
|
||||
}
|
||||
return (pidfile => $pidfile);
|
||||
}
|
||||
|
||||
# All config other than the pidfile has to be done in gd_getopt
|
||||
# in order for it to be set up early enough.
|
||||
sub gd_getopt {
|
||||
my $self = shift;
|
||||
|
||||
$self->SUPER::gd_getopt();
|
||||
|
||||
if ($self->{gd_args}{progname}) {
|
||||
$self->{gd_progname} = $self->{gd_args}{progname};
|
||||
}
|
||||
else {
|
||||
$self->{gd_progname} = basename($0);
|
||||
}
|
||||
|
||||
# There are places that Daemon Generic's new() uses $0 instead of
|
||||
# gd_progname, which it really shouldn't, but this hack fixes it.
|
||||
$self->{_original_zero} = $0;
|
||||
$0 = $self->{gd_progname};
|
||||
}
|
||||
|
||||
sub gd_postconfig {
|
||||
my $self = shift;
|
||||
# See the hack above in gd_getopt. This just reverses it
|
||||
# in case anything else needs the accurate $0.
|
||||
$0 = delete $self->{_original_zero};
|
||||
}
|
||||
|
||||
sub gd_more_opt {
|
||||
my $self = shift;
|
||||
return (
|
||||
'pidfile=s' => \$self->{gd_args}{pidfile},
|
||||
'n=s' => \$self->{gd_args}{progname},
|
||||
);
|
||||
}
|
||||
|
||||
sub gd_usage {
|
||||
pod2usage({ -verbose => 0, -exitval => 'NOEXIT' });
|
||||
return 0
|
||||
}
|
||||
|
||||
sub gd_can_install {
|
||||
my $self = shift;
|
||||
|
||||
my $source_file;
|
||||
if ( -e "/etc/SuSE-release" ) {
|
||||
$source_file = "contrib/$initscript.suse";
|
||||
} else {
|
||||
$source_file = "contrib/$initscript.rhel";
|
||||
}
|
||||
my $dest_file = "$initd/$initscript";
|
||||
my $sysconfig = '/etc/sysconfig';
|
||||
my $config_file = "$sysconfig/$initscript";
|
||||
|
||||
if (!-x $chkconfig or !-d $initd) {
|
||||
return $self->SUPER::gd_can_install(@_);
|
||||
}
|
||||
|
||||
return sub {
|
||||
if (!-w $initd) {
|
||||
print "You must run the 'install' command as root.\n";
|
||||
return;
|
||||
}
|
||||
if (-e $dest_file) {
|
||||
print "$initscript already in $initd.\n";
|
||||
}
|
||||
else {
|
||||
copy($source_file, $dest_file)
|
||||
or die "Could not copy $source_file to $dest_file: $!";
|
||||
chmod(0755, $dest_file)
|
||||
or die "Could not change permissions on $dest_file: $!";
|
||||
}
|
||||
|
||||
system($chkconfig, '--add', $initscript);
|
||||
print "$initscript installed.",
|
||||
" To start the daemon, do \"$dest_file start\" as root.\n";
|
||||
|
||||
if (-d $sysconfig and -w $sysconfig) {
|
||||
if (-e $config_file) {
|
||||
print "$config_file already exists.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
open(my $config_fh, ">", $config_file)
|
||||
or die "Could not write to $config_file: $!";
|
||||
my $directory = abs_path(dirname($self->{_original_zero}));
|
||||
my $owner_id = (stat $self->{_original_zero})[4];
|
||||
my $owner = getpwuid($owner_id);
|
||||
print $config_fh <<END;
|
||||
#!/bin/sh
|
||||
BUGZILLA="$directory"
|
||||
USER=$owner
|
||||
END
|
||||
close($config_fh);
|
||||
}
|
||||
else {
|
||||
print "Please edit $dest_file to configure the daemon.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub gd_can_uninstall {
|
||||
my $self = shift;
|
||||
|
||||
if (-x $chkconfig and -d $initd) {
|
||||
return sub {
|
||||
if (!-e "$initd/$initscript") {
|
||||
print "$initscript not installed.\n";
|
||||
return;
|
||||
}
|
||||
system($chkconfig, '--del', $initscript);
|
||||
print "$initscript disabled.",
|
||||
" To stop it, run: $initd/$initscript stop\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $self->SUPER::gd_can_install(@_);
|
||||
}
|
||||
|
||||
sub gd_check {
|
||||
my $self = shift;
|
||||
|
||||
# Get a count of all the jobs currently in the queue.
|
||||
my $jq = Bugzilla->job_queue();
|
||||
my @dbs = $jq->bz_databases();
|
||||
my $count = 0;
|
||||
foreach my $driver (@dbs) {
|
||||
$count += $driver->select_one('SELECT COUNT(*) FROM ts_job', []);
|
||||
}
|
||||
print get_text('job_queue_depth', { count => $count }) . "\n";
|
||||
}
|
||||
|
||||
sub gd_setup_signals {
|
||||
my $self = shift;
|
||||
$self->SUPER::gd_setup_signals();
|
||||
$SIG{TERM} = sub { $self->gd_quit_event(); }
|
||||
}
|
||||
|
||||
sub gd_run {
|
||||
my $self = shift;
|
||||
|
||||
my $jq = Bugzilla->job_queue();
|
||||
$jq->set_verbose($self->{debug});
|
||||
foreach my $module (values %{ Bugzilla::JobQueue::JOB_MAP() }) {
|
||||
eval "use $module";
|
||||
$jq->can_do($module);
|
||||
}
|
||||
$jq->work;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::JobQueue::Runner - A class representing the daemon that runs the
|
||||
job queue.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::JobQueue::Runner;
|
||||
Bugzilla::JobQueue::Runner->new();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is a subclass of L<Daemon::Generic> that is used by L<jobqueue>
|
||||
to run the Bugzilla job queue.
|
||||
@@ -1,176 +0,0 @@
|
||||
# -*- 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): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::Keyword;
|
||||
|
||||
use base qw(Bugzilla::Object);
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
keyworddefs.id
|
||||
keyworddefs.name
|
||||
keyworddefs.description
|
||||
);
|
||||
|
||||
use constant DB_TABLE => 'keyworddefs';
|
||||
|
||||
use constant VALIDATORS => {
|
||||
name => \&_check_name,
|
||||
description => \&_check_description,
|
||||
};
|
||||
|
||||
use constant UPDATE_COLUMNS => qw(
|
||||
name
|
||||
description
|
||||
);
|
||||
|
||||
###############################
|
||||
#### Accessors ######
|
||||
###############################
|
||||
|
||||
sub description { return $_[0]->{'description'}; }
|
||||
|
||||
sub bug_count {
|
||||
my ($self) = @_;
|
||||
return $self->{'bug_count'} if defined $self->{'bug_count'};
|
||||
($self->{'bug_count'}) =
|
||||
Bugzilla->dbh->selectrow_array(
|
||||
'SELECT COUNT(*) FROM keywords WHERE keywordid = ?',
|
||||
undef, $self->id);
|
||||
return $self->{'bug_count'};
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Mutators #####
|
||||
###############################
|
||||
|
||||
sub set_name { $_[0]->set('name', $_[1]); }
|
||||
sub set_description { $_[0]->set('description', $_[1]); }
|
||||
|
||||
###############################
|
||||
#### Subroutines ######
|
||||
###############################
|
||||
|
||||
sub get_all_with_bug_count {
|
||||
my $class = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $keywords =
|
||||
$dbh->selectall_arrayref('SELECT '
|
||||
. join(', ', $class->_get_db_columns) . ',
|
||||
COUNT(keywords.bug_id) AS bug_count
|
||||
FROM keyworddefs
|
||||
LEFT JOIN keywords
|
||||
ON keyworddefs.id = keywords.keywordid ' .
|
||||
$dbh->sql_group_by('keyworddefs.id',
|
||||
'keyworddefs.name,
|
||||
keyworddefs.description') . '
|
||||
ORDER BY keyworddefs.name', {'Slice' => {}});
|
||||
if (!$keywords) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach my $keyword (@$keywords) {
|
||||
bless($keyword, $class);
|
||||
}
|
||||
return $keywords;
|
||||
}
|
||||
|
||||
###############################
|
||||
### Validators ###
|
||||
###############################
|
||||
|
||||
sub _check_name {
|
||||
my ($self, $name) = @_;
|
||||
|
||||
$name = trim($name);
|
||||
if (!defined $name or $name eq "") {
|
||||
ThrowUserError("keyword_blank_name");
|
||||
}
|
||||
if ($name =~ /[\s,]/) {
|
||||
ThrowUserError("keyword_invalid_name");
|
||||
}
|
||||
|
||||
# We only want to validate the non-existence of the name if
|
||||
# we're creating a new Keyword or actually renaming the keyword.
|
||||
if (!ref($self) || $self->name ne $name) {
|
||||
my $keyword = new Bugzilla::Keyword({ name => $name });
|
||||
ThrowUserError("keyword_already_exists", { name => $name }) if $keyword;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub _check_description {
|
||||
my ($self, $desc) = @_;
|
||||
$desc = trim($desc);
|
||||
if (!defined $desc or $desc eq '') {
|
||||
ThrowUserError("keyword_blank_description");
|
||||
}
|
||||
return $desc;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Keyword - A Keyword that can be added to a bug.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Keyword;
|
||||
|
||||
my $description = $keyword->description;
|
||||
|
||||
my $keywords = Bugzilla::Keyword->get_all_with_bug_count();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Bugzilla::Keyword represents a keyword that can be added to a bug.
|
||||
|
||||
This implements all standard C<Bugzilla::Object> methods. See
|
||||
L<Bugzilla::Object> for more details.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
This is only a list of subroutines specific to C<Bugzilla::Keyword>.
|
||||
See L<Bugzilla::Object> for more subroutines that this object
|
||||
implements.
|
||||
|
||||
=over
|
||||
|
||||
=item C<get_all_with_bug_count()>
|
||||
|
||||
Description: Returns all defined keywords. This is an efficient way
|
||||
to get the associated bug counts, as only one SQL query
|
||||
is executed with this method, instead of one per keyword
|
||||
when calling get_all and then bug_count.
|
||||
Params: none
|
||||
Returns: A reference to an array of Keyword objects, or an empty
|
||||
arrayref if there are no keywords.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user