summaryrefslogtreecommitdiff
path: root/rt
diff options
context:
space:
mode:
Diffstat (limited to 'rt')
-rwxr-xr-xrt/bin/rt2
-rw-r--r--rt/bin/rt.in2
-rwxr-xr-xrt/configure149
-rw-r--r--rt/docs/web_deployment.pod2
-rw-r--r--rt/etc/RT_Config.pm.in24
-rw-r--r--rt/etc/RT_SiteConfig.pm2
-rw-r--r--rt/etc/initialdata2
-rw-r--r--rt/etc/schema.SQLite106
-rw-r--r--rt/etc/upgrade/3.3.0/schema.mysql53
-rw-r--r--rt/etc/upgrade/3.3.11/schema.mysql4
-rw-r--r--rt/etc/upgrade/3.9.5/schema.mysql24
-rw-r--r--rt/etc/upgrade/3.9.7/schema.mysql10
-rw-r--r--rt/lib/RT.pm11
-rw-r--r--rt/lib/RT.pm.in719
-rw-r--r--rt/lib/RT/Action/CreateTickets.pm3
-rw-r--r--rt/lib/RT/Articles.pm3
-rw-r--r--rt/lib/RT/Config.pm6
-rw-r--r--rt/lib/RT/Crypt/GnuPG.pm1
-rw-r--r--rt/lib/RT/Dashboard.pm30
-rw-r--r--rt/lib/RT/Generated.pm2
-rw-r--r--rt/lib/RT/I18N.pm6
-rwxr-xr-xrt/lib/RT/Interface/Email.pm39
-rw-r--r--rt/lib/RT/Interface/Web.pm44
-rwxr-xr-xrt/lib/RT/Record.pm4
-rwxr-xr-xrt/lib/RT/Scrip.pm2
-rwxr-xr-xrt/lib/RT/Scrips.pm80
-rw-r--r--rt/lib/RT/Search/Googleish.pm16
-rw-r--r--rt/lib/RT/SearchBuilder.pm40
-rw-r--r--rt/lib/RT/Shredder.pm6
-rw-r--r--rt/lib/RT/Test.pm6
-rwxr-xr-xrt/lib/RT/Ticket.pm76
-rwxr-xr-xrt/lib/RT/Tickets.pm11
-rwxr-xr-xrt/lib/RT/Transaction.pm11
-rw-r--r--rt/lib/RT/URI.pm19
-rwxr-xr-xrt/lib/RT/User.pm2
-rw-r--r--rt/sbin/rt-server.fcgi.in2
-rw-r--r--rt/sbin/rt-server.in2
-rw-r--r--rt/sbin/rt-test-dependencies.in15
-rwxr-xr-xrt/sbin/standalone_httpd2
-rw-r--r--rt/sbin/standalone_httpd.in2
-rwxr-xr-xrt/share/html/Admin/Queues/Modify.html2
-rwxr-xr-xrt/share/html/Approvals/Elements/PendingMyApproval2
-rw-r--r--rt/share/html/Approvals/autohandler9
-rw-r--r--rt/share/html/Dashboards/Subscription.html2
-rw-r--r--rt/share/html/Elements/AddCustomers2
-rw-r--r--rt/share/html/Elements/ColumnMap8
-rw-r--r--rt/share/html/Elements/EditCustomField2
-rwxr-xr-xrt/share/html/Elements/Header3
-rw-r--r--rt/share/html/Elements/HeaderJavascript2
-rwxr-xr-xrt/share/html/Elements/ListActions2
-rwxr-xr-xrt/share/html/Elements/MessageBox3
-rw-r--r--rt/share/html/Elements/QueueSummaryByStatus10
-rw-r--r--rt/share/html/Elements/RT__CustomField/ColumnMap4
-rwxr-xr-xrt/share/html/Elements/SelectWatcherType2
-rwxr-xr-xrt/share/html/Elements/Tabs2
-rw-r--r--rt/share/html/Helpers/Autocomplete/Users3
-rw-r--r--rt/share/html/NoAuth/css/aileron/boxes.css4
-rw-r--r--rt/share/html/NoAuth/css/aileron/ticket.css13
-rw-r--r--rt/share/html/NoAuth/css/ballard/boxes.css5
-rw-r--r--rt/share/html/NoAuth/css/ballard/layout.css4
-rw-r--r--rt/share/html/NoAuth/css/ballard/nav.css3
-rw-r--r--rt/share/html/NoAuth/css/ballard/ticket-search.css1
-rw-r--r--rt/share/html/NoAuth/css/ballard/ticket.css3
-rw-r--r--rt/share/html/NoAuth/css/base/forms.css1
-rw-r--r--rt/share/html/NoAuth/css/base/jquery-ui-timepicker-addon.css7
-rw-r--r--rt/share/html/NoAuth/css/base/jquery-ui.css2
-rw-r--r--rt/share/html/NoAuth/css/base/jquery-ui.custom.modified.css24
-rw-r--r--rt/share/html/NoAuth/css/base/main.css1
-rw-r--r--rt/share/html/NoAuth/css/base/superfish-navbar.css2
-rw-r--r--rt/share/html/NoAuth/css/base/superfish.css2
-rw-r--r--rt/share/html/NoAuth/css/base/ticket-form.css31
-rw-r--r--rt/share/html/NoAuth/css/base/ui.timepickr.css56
-rw-r--r--rt/share/html/NoAuth/css/base/ui.timepickr.custom.css54
-rw-r--r--rt/share/html/NoAuth/css/web2/nav.css1
-rw-r--r--rt/share/html/NoAuth/js/jquery-ui-1.8.4.custom.min.js50
-rw-r--r--rt/share/html/NoAuth/js/jquery-ui-patch-datepicker.js31
-rw-r--r--rt/share/html/NoAuth/js/jquery-ui-timepicker-addon.js1326
-rw-r--r--rt/share/html/NoAuth/js/ui.timepickr.js941
-rw-r--r--rt/share/html/NoAuth/js/util.js66
-rw-r--r--rt/share/html/Prefs/Other.html1
-rwxr-xr-xrt/share/html/REST/1.0/Forms/ticket/default22
-rw-r--r--rt/share/html/Search/Chart.html8
-rw-r--r--rt/share/html/Search/Elements/SelectPersonType2
-rwxr-xr-xrt/share/html/Search/Results.html1
-rw-r--r--rt/share/html/Search/Results.xls1
-rwxr-xr-xrt/share/html/Ticket/Attachment/dhandler9
-rw-r--r--rt/share/html/Ticket/Elements/AddCustomers2
-rwxr-xr-xrt/share/html/Ticket/Elements/ShowMembers7
-rw-r--r--rt/share/html/Ticket/Elements/ShowTransactionAttachments18
-rw-r--r--rt/share/html/m/_elements/raw_style4
-rw-r--r--rt/share/html/m/_elements/wrapper2
-rw-r--r--rt/t/api/config.t12
-rw-r--r--rt/t/api/template-insert.t26
-rw-r--r--rt/t/api/template-simple.t275
-rw-r--r--rt/t/api/template.t45
-rw-r--r--rt/t/articles/search-interface.t61
-rw-r--r--rt/t/articles/uri-a.t38
-rw-r--r--rt/t/data/configs/apache2.2+fastcgi.conf.in1
-rw-r--r--rt/t/data/configs/apache2.2+mod_perl.conf.in1
-rw-r--r--rt/t/mail/dashboard-chart-with-utf8.t12
-rw-r--r--rt/t/mail/dashboards.t30
-rw-r--r--rt/t/mail/gateway.t31
-rw-r--r--rt/t/shredder/01ticket.t6
-rw-r--r--rt/t/shredder/03plugin_tickets.t5
-rw-r--r--rt/t/shredder/03plugin_users.t62
-rw-r--r--rt/t/shredder/utils.pl2
-rw-r--r--rt/t/ticket/search_by_watcher.t10
-rw-r--r--rt/t/web/attachments.t23
-rw-r--r--rt/t/web/command_line.t4
-rw-r--r--rt/t/web/command_line_with_unknown_field.t7
-rw-r--r--rt/t/web/crypt-gnupg.t4
-rw-r--r--rt/t/web/googleish_search.t6
-rw-r--r--rt/t/web/query_builder_queue_limits.t3
-rw-r--r--rt/t/web/search_simple.t56
-rw-r--r--rt/t/web/ticket_modify_all.t43
-rw-r--r--rt/t/web/transaction_batch.t9
116 files changed, 2509 insertions, 2566 deletions
diff --git a/rt/bin/rt b/rt/bin/rt
index 32f459a7e..89873f5d6 100755
--- a/rt/bin/rt
+++ b/rt/bin/rt
@@ -420,7 +420,7 @@ sub show {
}
elsif (my $spec = is_object_spec($_, $type)) {
push @objects, $spec;
- $rawprint = 1 if $_ =~ /\/content$/ or $_ !~ /^ticket/;
+ $rawprint = 1 if $_ =~ /\/content$/ or $_ =~ /\/links/ or $_ !~ /^ticket/;
}
else {
my $datum = /^-/ ? "option" : "argument";
diff --git a/rt/bin/rt.in b/rt/bin/rt.in
index e54a07add..2a9f643c8 100644
--- a/rt/bin/rt.in
+++ b/rt/bin/rt.in
@@ -420,7 +420,7 @@ sub show {
}
elsif (my $spec = is_object_spec($_, $type)) {
push @objects, $spec;
- $rawprint = 1 if $_ =~ /\/content$/ or $_ !~ /^ticket/;
+ $rawprint = 1 if $_ =~ /\/content$/ or $_ =~ /\/links/ or $_ !~ /^ticket/;
}
else {
my $datum = /^-/ ? "option" : "argument";
diff --git a/rt/configure b/rt/configure
index 1862c5fe6..76ef85b92 100755
--- a/rt/configure
+++ b/rt/configure
@@ -1,7 +1,7 @@
#! /bin/sh
# From configure.ac Revision.
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.67 for RT rt-4.0.6.
+# Generated by GNU Autoconf 2.68 for RT rt-4.0.7.
#
# Report bugs to <rt-bugs@bestpractical.com>.
#
@@ -92,6 +92,7 @@ fi
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -216,11 +217,18 @@ IFS=$as_save_IFS
# We cannot yet assume a decent shell, so we have to provide a
# neutralization value for shells without unset; and this also
# works around shells that cannot unset nonexistent variables.
+ # Preserve -v and -x to the replacement shell.
BASH_ENV=/dev/null
ENV=/dev/null
(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
export CONFIG_SHELL
- exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+ case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+ esac
+ exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
fi
if test x$as_have_required = xno; then :
@@ -552,8 +560,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='RT'
PACKAGE_TARNAME='rt'
-PACKAGE_VERSION='rt-4.0.6'
-PACKAGE_STRING='RT rt-4.0.6'
+PACKAGE_VERSION='rt-4.0.7'
+PACKAGE_STRING='RT rt-4.0.7'
PACKAGE_BUGREPORT='rt-bugs@bestpractical.com'
PACKAGE_URL=''
@@ -1165,7 +1173,7 @@ Try \`$0 --help' for more information"
$as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
$as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
- : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
;;
esac
@@ -1303,7 +1311,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures RT rt-4.0.6 to adapt to many kinds of systems.
+\`configure' configures RT rt-4.0.7 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1364,7 +1372,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of RT rt-4.0.6:";;
+ short | recursive ) echo "Configuration of RT rt-4.0.7:";;
esac
cat <<\_ACEOF
@@ -1488,8 +1496,8 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-RT configure rt-4.0.6
-generated by GNU Autoconf 2.67
+RT configure rt-4.0.7
+generated by GNU Autoconf 2.68
Copyright (C) 2010 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
@@ -1535,7 +1543,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_compile
@@ -1581,7 +1589,7 @@ fi
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_link
@@ -1589,8 +1597,8 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by RT $as_me rt-4.0.6, which was
-generated by GNU Autoconf 2.67. Invocation command line was
+It was created by RT $as_me rt-4.0.7, which was
+generated by GNU Autoconf 2.68. Invocation command line was
$ $0 $@
@@ -1848,7 +1856,7 @@ $as_echo "$as_me: loading site script $ac_site_file" >&6;}
|| { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "failed to load site script $ac_site_file
-See \`config.log' for more details" "$LINENO" 5 ; }
+See \`config.log' for more details" "$LINENO" 5; }
fi
done
@@ -1946,7 +1954,7 @@ rt_version_major=4
rt_version_minor=0
-rt_version_patch=6
+rt_version_patch=7
test "x$rt_version_major" = 'x' && rt_version_major=0
test "x$rt_version_minor" = 'x' && rt_version_minor=0
@@ -1998,7 +2006,7 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
$as_echo_n "checking for a BSD-compatible install... " >&6; }
if test -z "$INSTALL"; then
-if test "${ac_cv_path_install+set}" = set; then :
+if ${ac_cv_path_install+:} false; then :
$as_echo_n "(cached) " >&6
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -2079,7 +2087,7 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
set dummy perl; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_path_PERL+set}" = set; then :
+if ${ac_cv_path_PERL+:} false; then :
$as_echo_n "(cached) " >&6
else
case $PERL in
@@ -2800,7 +2808,7 @@ if test -n "$ac_tool_prefix"; then
set dummy ${ac_tool_prefix}gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2840,7 +2848,7 @@ if test -z "$ac_cv_prog_CC"; then
set dummy gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
@@ -2893,7 +2901,7 @@ if test -z "$CC"; then
set dummy ${ac_tool_prefix}cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2933,7 +2941,7 @@ if test -z "$CC"; then
set dummy cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2992,7 +3000,7 @@ if test -z "$CC"; then
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -3036,7 +3044,7 @@ do
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
@@ -3091,7 +3099,7 @@ fi
test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
-See \`config.log' for more details" "$LINENO" 5 ; }
+See \`config.log' for more details" "$LINENO" 5; }
# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
@@ -3206,7 +3214,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "C compiler cannot create executables
-See \`config.log' for more details" "$LINENO" 5 ; }
+See \`config.log' for more details" "$LINENO" 5; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
@@ -3249,7 +3257,7 @@ else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of executables: cannot compile and link
-See \`config.log' for more details" "$LINENO" 5 ; }
+See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest conftest$ac_cv_exeext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
@@ -3308,7 +3316,7 @@ $as_echo "$ac_try_echo"; } >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot run C compiled programs.
If you meant to cross compile, use \`--host'.
-See \`config.log' for more details" "$LINENO" 5 ; }
+See \`config.log' for more details" "$LINENO" 5; }
fi
fi
fi
@@ -3319,7 +3327,7 @@ rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
ac_clean_files=$ac_clean_files_save
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
$as_echo_n "checking for suffix of object files... " >&6; }
-if test "${ac_cv_objext+set}" = set; then :
+if ${ac_cv_objext+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -3360,7 +3368,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of object files: cannot compile
-See \`config.log' for more details" "$LINENO" 5 ; }
+See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
@@ -3370,7 +3378,7 @@ OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
-if test "${ac_cv_c_compiler_gnu+set}" = set; then :
+if ${ac_cv_c_compiler_gnu+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -3407,7 +3415,7 @@ ac_test_CFLAGS=${CFLAGS+set}
ac_save_CFLAGS=$CFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
$as_echo_n "checking whether $CC accepts -g... " >&6; }
-if test "${ac_cv_prog_cc_g+set}" = set; then :
+if ${ac_cv_prog_cc_g+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_save_c_werror_flag=$ac_c_werror_flag
@@ -3485,7 +3493,7 @@ else
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
-if test "${ac_cv_prog_cc_c89+set}" = set; then :
+if ${ac_cv_prog_cc_c89+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_prog_cc_c89=no
@@ -3583,7 +3591,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for aginitlib in -lgraph" >&5
$as_echo_n "checking for aginitlib in -lgraph... " >&6; }
-if test "${ac_cv_lib_graph_aginitlib+set}" = set; then :
+if ${ac_cv_lib_graph_aginitlib+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -3617,7 +3625,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_graph_aginitlib" >&5
$as_echo "$ac_cv_lib_graph_aginitlib" >&6; }
-if test "x$ac_cv_lib_graph_aginitlib" = x""yes; then :
+if test "x$ac_cv_lib_graph_aginitlib" = xyes; then :
RT_GRAPHVIZ="1"
fi
@@ -3643,7 +3651,7 @@ fi
set dummy gdlib-config; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_RT_GD+set}" = set; then :
+if ${ac_cv_prog_RT_GD+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$RT_GD"; then
@@ -3699,7 +3707,7 @@ fi
set dummy gpg; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_RT_GPG+set}" = set; then :
+if ${ac_cv_prog_RT_GPG+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$RT_GPG"; then
@@ -3984,10 +3992,21 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
:end' >>confcache
if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
if test -w "$cache_file"; then
- test "x$cache_file" != "x/dev/null" &&
+ if test "x$cache_file" != "x/dev/null"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
$as_echo "$as_me: updating cache $cache_file" >&6;}
- cat confcache >$cache_file
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
@@ -4055,7 +4074,7 @@ LTLIBOBJS=$ac_ltlibobjs
-: ${CONFIG_STATUS=./config.status}
+: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files $CONFIG_STATUS"
@@ -4156,6 +4175,7 @@ fi
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -4462,8 +4482,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by RT $as_me rt-4.0.6, which was
-generated by GNU Autoconf 2.67. Invocation command line was
+This file was extended by RT $as_me rt-4.0.7, which was
+generated by GNU Autoconf 2.68. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -4515,8 +4535,8 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-RT config.status rt-4.0.6
-configured by $0, generated by GNU Autoconf 2.67,
+RT config.status rt-4.0.7
+configured by $0, generated by GNU Autoconf 2.68,
with options \\"\$ac_cs_config\\"
Copyright (C) 2010 Free Software Foundation, Inc.
@@ -4658,7 +4678,7 @@ do
"t/data/configs/apache2.2+mod_perl.conf") CONFIG_FILES="$CONFIG_FILES t/data/configs/apache2.2+mod_perl.conf" ;;
"t/data/configs/apache2.2+fastcgi.conf") CONFIG_FILES="$CONFIG_FILES t/data/configs/apache2.2+fastcgi.conf" ;;
- *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5 ;;
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
done
@@ -4679,9 +4699,10 @@ fi
# after its creation but before its name has been assigned to `$tmp'.
$debug ||
{
- tmp=
+ tmp= ac_tmp=
trap 'exit_status=$?
- { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
' 0
trap 'as_fn_exit 1' 1 2 13 15
}
@@ -4689,12 +4710,13 @@ $debug ||
{
tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
- test -n "$tmp" && test -d "$tmp"
+ test -d "$tmp"
} ||
{
tmp=./conf$$-$RANDOM
(umask 077 && mkdir "$tmp")
} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
# Set up the scripts for CONFIG_FILES section.
# No need to generate them if there are no CONFIG_FILES.
@@ -4716,7 +4738,7 @@ else
ac_cs_awk_cr=$ac_cr
fi
-echo 'BEGIN {' >"$tmp/subs1.awk" &&
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
_ACEOF
@@ -4744,7 +4766,7 @@ done
rm -f conf$$subs.sh
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
-cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
_ACEOF
sed -n '
h
@@ -4792,7 +4814,7 @@ t delim
rm -f conf$$subs.awk
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
_ACAWK
-cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
for (key in S) S_is_set[key] = 1
FS = ""
@@ -4824,7 +4846,7 @@ if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
else
cat
-fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
|| as_fn_error $? "could not setup config files machinery" "$LINENO" 5
_ACEOF
@@ -4864,7 +4886,7 @@ do
esac
case $ac_mode$ac_tag in
:[FHL]*:*);;
- :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5 ;;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
:[FH]-) ac_tag=-:-;;
:[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
esac
@@ -4883,7 +4905,7 @@ do
for ac_f
do
case $ac_f in
- -) ac_f="$tmp/stdin";;
+ -) ac_f="$ac_tmp/stdin";;
*) # Look for the file first in the build tree, then in the source tree
# (if the path is not absolute). The absolute path cannot be DOS-style,
# because $ac_f cannot contain `:'.
@@ -4892,7 +4914,7 @@ do
[\\/$]*) false;;
*) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
esac ||
- as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5 ;;
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
esac
case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
as_fn_append ac_file_inputs " '$ac_f'"
@@ -4918,8 +4940,8 @@ $as_echo "$as_me: creating $ac_file" >&6;}
esac
case $ac_tag in
- *:-:* | *:-) cat >"$tmp/stdin" \
- || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
esac
;;
esac
@@ -5049,21 +5071,22 @@ s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
s&@INSTALL@&$ac_INSTALL&;t t
$ac_datarootdir_hack
"
-eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
- || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
- { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
- { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
which seems to be undefined. Please make sure it is defined" >&5
$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
which seems to be undefined. Please make sure it is defined" >&2;}
- rm -f "$tmp/stdin"
+ rm -f "$ac_tmp/stdin"
case $ac_file in
- -) cat "$tmp/out" && rm -f "$tmp/out";;
- *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
esac \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
;;
diff --git a/rt/docs/web_deployment.pod b/rt/docs/web_deployment.pod
index 4c3f73fb5..5d2cd4c00 100644
--- a/rt/docs/web_deployment.pod
+++ b/rt/docs/web_deployment.pod
@@ -23,7 +23,7 @@ to use L<Starman>, a high performance preforking server:
/opt/rt4/sbin/rt-server --server Starman --port 8080
B<NOTICE>: After you run the standalone server as root, you will need to
-remove your C<var/mason> directory, or the non-standalone servers
+remove your C<var/mason_data> directory, or the non-standalone servers
(Apache, etc), which run as a non-privileged user, will not be able to
write to it and will not work.
diff --git a/rt/etc/RT_Config.pm.in b/rt/etc/RT_Config.pm.in
index bd48b6efd..169182033 100644
--- a/rt/etc/RT_Config.pm.in
+++ b/rt/etc/RT_Config.pm.in
@@ -640,6 +640,9 @@ Set($NotifyActor, 0);
By default, RT records each message it sends out to its own internal
database. To change this behavior, set C<$RecordOutgoingEmail> to 0
+If this is disabled, users' digest mail delivery preferences
+(i.e. EmailFrequency) will also be ignored.
+
=cut
Set($RecordOutgoingEmail, 1);
@@ -897,8 +900,8 @@ Set(@JSFiles, qw/
jquery-1.4.2.min.js
jquery_noconflict.js
jquery-ui-1.8.4.custom.min.js
+ jquery-ui-timepicker-addon.js
jquery-ui-patch-datepicker.js
- ui.timepickr.js
titlebox-state.js
util.js
userautocomplete.js
@@ -1826,6 +1829,16 @@ If the "RT has detected a possible cross-site request forgery" error is triggere
by a host:port sent by your browser that you believe should be valid, you can copy
the host:port from the error message into this list.
+Simple wildcards, similar to SSL certificates, are allowed. For example:
+
+ *.example.com:80 # matches foo.example.com
+ # but not example.com
+ # or foo.bar.example.com
+
+ www*.example.com:80 # matches www3.example.com
+ # and www-test.example.com
+ # and www.example.com
+
=cut
Set(@ReferrerWhitelist, qw());
@@ -2279,10 +2292,11 @@ all possible transitions in each lifecycle using the following format:
=head3 Statuses available during ticket creation
-By default users can create tickets with any status, except
-deleted. If you want to restrict statuses available during creation
-then describe transition from '' (empty string), like in the example
-above.
+By default users can create tickets with a status of new,
+open, or resolved, but cannot create tickets with a status of
+rejected, stalled, or deleted. If you want to change the statuses
+available during creation, update the transition from '' (empty
+string), like in the example above.
=head3 Protecting status changes with rights
diff --git a/rt/etc/RT_SiteConfig.pm b/rt/etc/RT_SiteConfig.pm
index 29a7d0231..4a397fa16 100644
--- a/rt/etc/RT_SiteConfig.pm
+++ b/rt/etc/RT_SiteConfig.pm
@@ -49,7 +49,7 @@ Set($MessageBoxWidth, 80);
Set($MessageBoxRichTextHeight, 368);
#redirects to ticket display on quick create
-#Set($QuickCreateRedirect, 1);
+#Set($DisplayTicketAfterQuickCreate, 1);
#Set(@Plugins,(qw(Extension::QuickDelete RT::FM)));
diff --git a/rt/etc/initialdata b/rt/etc/initialdata
index cc07cec59..7ab746db1 100644
--- a/rt/etc/initialdata
+++ b/rt/etc/initialdata
@@ -1,4 +1,4 @@
-# Initial data for a fresh RT3 Installation.
+# Initial data for a fresh RT installation.
@Users = (
{ Name => 'root',
diff --git a/rt/etc/schema.SQLite b/rt/etc/schema.SQLite
index 138971cfb..6897be2d6 100644
--- a/rt/etc/schema.SQLite
+++ b/rt/etc/schema.SQLite
@@ -3,7 +3,7 @@
CREATE TABLE Attachments (
id INTEGER PRIMARY KEY ,
TransactionId INTEGER ,
- Parent integer NULL ,
+ Parent integer NULL DEFAULT 0 ,
MessageId varchar(160) NULL ,
Subject varchar(255) NULL ,
Filename varchar(255) NULL ,
@@ -11,7 +11,7 @@ CREATE TABLE Attachments (
ContentEncoding varchar(80) NULL ,
Content LONGTEXT NULL ,
Headers LONGTEXT NULL ,
- Creator integer NULL ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL
) ;
@@ -30,12 +30,12 @@ CREATE TABLE Queues (
CommentAddress varchar(120) NULL ,
Lifecycle varchar(32) NULL ,
SubjectTag varchar(120) NULL ,
- InitialPriority integer NULL ,
- FinalPriority integer NULL ,
- DefaultDueIn integer NULL ,
- Creator integer NULL ,
+ InitialPriority integer NULL DEFAULT 0 ,
+ FinalPriority integer NULL DEFAULT 0 ,
+ DefaultDueIn integer NULL DEFAULT 0 ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL ,
- LastUpdatedBy integer NULL ,
+ LastUpdatedBy integer NULL DEFAULT 0 ,
LastUpdated DATETIME NULL ,
Disabled int2 NOT NULL DEFAULT 0
@@ -51,11 +51,11 @@ CREATE TABLE Links (
Base varchar(240) NULL ,
Target varchar(240) NULL ,
Type varchar(20) NOT NULL ,
- LocalTarget integer NULL ,
- LocalBase integer NULL ,
- LastUpdatedBy integer NULL ,
+ LocalTarget integer NULL DEFAULT 0 ,
+ LocalBase integer NULL DEFAULT 0 ,
+ LastUpdatedBy integer NULL DEFAULT 0 ,
LastUpdated DATETIME NULL ,
- Creator integer NULL ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL
) ;
@@ -106,9 +106,9 @@ CREATE TABLE ScripConditions (
Argument varchar(255) NULL ,
ApplicableTransTypes varchar(60) NULL ,
- Creator integer NULL ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL ,
- LastUpdatedBy integer NULL ,
+ LastUpdatedBy integer NULL DEFAULT 0 ,
LastUpdated DATETIME NULL
) ;
@@ -119,8 +119,8 @@ CREATE TABLE ScripConditions (
CREATE TABLE Transactions (
id INTEGER PRIMARY KEY ,
ObjectType varchar(255) NULL ,
- ObjectId integer NULL ,
- TimeTaken integer NULL ,
+ ObjectId integer NULL DEFAULT 0 ,
+ TimeTaken integer NULL DEFAULT 0 ,
Type varchar(20) NULL ,
Field varchar(40) NULL ,
OldValue varchar(255) NULL ,
@@ -130,7 +130,7 @@ CREATE TABLE Transactions (
NewReference integer NULL ,
Data varchar(255) NULL ,
- Creator integer NULL ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL
) ;
@@ -143,19 +143,19 @@ CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
CREATE TABLE Scrips (
id INTEGER PRIMARY KEY ,
Description varchar(255),
- ScripCondition integer NULL ,
- ScripAction integer NULL ,
+ ScripCondition integer NULL DEFAULT 0 ,
+ ScripAction integer NULL DEFAULT 0 ,
ConditionRules text NULL ,
ActionRules text NULL ,
CustomIsApplicableCode text NULL ,
CustomPrepareCode text NULL ,
CustomCommitCode text NULL ,
Stage varchar(32) NULL ,
- Queue integer NULL ,
- Template integer NULL ,
- Creator integer NULL ,
+ Queue integer NULL DEFAULT 0 ,
+ Template integer NULL DEFAULT 0 ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL ,
- LastUpdatedBy integer NULL ,
+ LastUpdatedBy integer NULL DEFAULT 0 ,
LastUpdated DATETIME NULL
) ;
@@ -167,7 +167,7 @@ CREATE TABLE ACL (
id INTEGER PRIMARY KEY ,
PrincipalType varchar(25) NOT NULL,
- PrincipalId INTEGER,
+ PrincipalId INTEGER DEFAULT 0,
RightName varchar(25) NOT NULL ,
ObjectType varchar(25) NOT NULL ,
ObjectId INTEGER default 0,
@@ -185,8 +185,8 @@ CREATE TABLE ACL (
CREATE TABLE GroupMembers (
id INTEGER PRIMARY KEY ,
- GroupId integer NULL,
- MemberId integer NULL,
+ GroupId integer NULL DEFAULT 0,
+ MemberId integer NULL DEFAULT 0,
Creator integer NOT NULL DEFAULT 0 ,
Created DATETIME NULL ,
LastUpdatedBy integer NOT NULL DEFAULT 0 ,
@@ -250,9 +250,9 @@ CREATE TABLE Users (
Timezone char(50) NULL ,
PGPKey text NULL,
- Creator integer NULL ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL ,
- LastUpdatedBy integer NULL ,
+ LastUpdatedBy integer NULL DEFAULT 0 ,
LastUpdated DATETIME NULL
) ;
@@ -270,20 +270,20 @@ CREATE INDEX Users4 ON Users (EmailAddress);
CREATE TABLE Tickets (
id INTEGER PRIMARY KEY ,
- EffectiveId integer NULL ,
- Queue integer NULL ,
+ EffectiveId integer NULL DEFAULT 0 ,
+ Queue integer NULL DEFAULT 0 ,
Type varchar(16) NULL ,
- IssueStatement integer NULL ,
- Resolution integer NULL ,
- Owner integer NULL ,
+ IssueStatement integer NULL DEFAULT 0 ,
+ Resolution integer NULL DEFAULT 0 ,
+ Owner integer NULL DEFAULT 0 ,
Subject varchar(200) NULL DEFAULT '[no subject]' ,
- InitialPriority integer NULL ,
- FinalPriority integer NULL ,
- Priority integer NULL ,
- TimeEstimated integer NULL ,
- TimeWorked integer NULL ,
+ InitialPriority integer NULL DEFAULT 0 ,
+ FinalPriority integer NULL DEFAULt 0 ,
+ Priority integer NULL DEFAULT 0 ,
+ TimeEstimated integer NULL DEFAULT 0 ,
+ TimeWorked integer NULL DEFAULT 0 ,
Status varchar(64) NULL ,
- TimeLeft integer NULL ,
+ TimeLeft integer NULL DEFAULT 0 ,
Told DATETIME NULL ,
Starts DATETIME NULL ,
Started DATETIME NULL ,
@@ -291,9 +291,9 @@ CREATE TABLE Tickets (
Resolved DATETIME NULL ,
- LastUpdatedBy integer NULL ,
+ LastUpdatedBy integer NULL DEFAULT 0 ,
LastUpdated DATETIME NULL ,
- Creator integer NULL ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL ,
Disabled int2 NOT NULL DEFAULT 0
@@ -315,9 +315,9 @@ CREATE TABLE ScripActions (
Description varchar(255) NULL ,
ExecModule varchar(60) NULL ,
Argument varchar(255) NULL ,
- Creator integer NULL ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL ,
- LastUpdatedBy integer NULL ,
+ LastUpdatedBy integer NULL DEFAULT 0 ,
LastUpdated DATETIME NULL
) ;
@@ -333,11 +333,11 @@ CREATE TABLE Templates (
Description varchar(255) NULL ,
Type varchar(16) NULL ,
Language varchar(16) NULL ,
- TranslationOf integer NULL ,
+ TranslationOf integer NULL DEFAULT 0 ,
Content blob NULL ,
LastUpdated DATETIME NULL ,
- LastUpdatedBy integer NULL ,
- Creator integer NULL ,
+ LastUpdatedBy integer NULL DEFAULT 0 ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL
) ;
@@ -437,10 +437,10 @@ CREATE TABLE Attributes (
Content LONGTEXT NULL ,
ContentType varchar(16),
ObjectType varchar(25) NOT NULL ,
- ObjectId INTEGER default 0,
- Creator integer NULL ,
+ ObjectId INTEGER ,
+ Creator integer NULL DEFAULT 0 ,
Created DATETIME NULL ,
- LastUpdatedBy integer NULL ,
+ LastUpdatedBy integer NULL DEFAULT 0 ,
LastUpdated DATETIME NULL
) ;
@@ -483,22 +483,22 @@ Parent integer NOT NULL DEFAULT 0,
Name varchar(255) NOT NULL DEFAULT '',
Description varchar(255) NOT NULL DEFAULT '',
ObjectType varchar(64) NOT NULL DEFAULT '',
-ObjectId integer NOT NULL
+ObjectId integer NOT NULL DEFAULT 0
);
CREATE TABLE ObjectTopics (
id INTEGER PRIMARY KEY,
-Topic integer NOT NULL,
+Topic integer NOT NULL DEFAULT 0,
ObjectType varchar(64) NOT NULL DEFAULT '',
-ObjectId integer NOT NULL
+ObjectId integer NOT NULL DEFAULT 0
);
CREATE TABLE ObjectClasses (
id INTEGER PRIMARY KEY,
-Class integer NOT NULL,
+Class integer NOT NULL DEFAULT 0,
ObjectType varchar(64) NOT NULL DEFAULT '',
-ObjectId integer NOT NULL,
+ObjectId integer NOT NULL DEFAULT 0,
Creator integer NOT NULL DEFAULT 0,
Created TIMESTAMP NULL,
LastUpdatedBy integer NOT NULL DEFAULT 0,
diff --git a/rt/etc/upgrade/3.3.0/schema.mysql b/rt/etc/upgrade/3.3.0/schema.mysql
index f6998363e..d8b04991e 100644
--- a/rt/etc/upgrade/3.3.0/schema.mysql
+++ b/rt/etc/upgrade/3.3.0/schema.mysql
@@ -1,37 +1,32 @@
-alter Table Transactions ADD Column (ObjectType varchar(64) not null);
-update Transactions set ObjectType = 'RT::Ticket';
-alter table Transactions drop column EffectiveTicket;
-alter table Transactions add column ReferenceType varchar(255) NULL;
-alter table Transactions add column OldReference integer NULL;
-alter table Transactions add column NewReference integer NULL;
-alter table Transactions drop index transactions1;
-alter table Transactions change Ticket ObjectId integer NOT NULL DEFAULT 0 ;
+drop index transactions1 ON Transactions;
-CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
-
-alter table TicketCustomFieldValues rename ObjectCustomFieldValues;
+alter Table Transactions
+ ADD COLUMN (ObjectType varchar(64) not null),
+ DROP COLUMN EffectiveTicket,
+ ADD COLUMN ReferenceType varchar(255) NULL,
+ ADD COLUMN OldReference integer NULL,
+ ADD COLUMN NewReference integer NULL,
+ CHANGE Ticket ObjectId integer NOT NULL DEFAULT 0;
-alter table ObjectCustomFieldValues change Ticket ObjectId integer NOT NULL DEFAULT 0 ;
+UPDATE Transactions set ObjectType = 'RT::Ticket';
+CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
-alter table ObjectCustomFieldValues add column ObjectType varchar(255) not null;
+alter table TicketCustomFieldValues rename ObjectCustomFieldValues,
+ change Ticket ObjectId integer NOT NULL DEFAULT 0 ,
+ add column ObjectType varchar(255) not null,
+ add column Current bool default 1,
+ add column LargeContent LONGTEXT NULL,
+ add column ContentType varchar(80) NULL,
+ add column ContentEncoding varchar(80) NULL;
update ObjectCustomFieldValues set ObjectType = 'RT::Ticket';
-alter table ObjectCustomFieldValues add column Current bool default 1;
-
-alter table ObjectCustomFieldValues add column LargeContent LONGTEXT NULL;
-
-alter table ObjectCustomFieldValues add column ContentType varchar(80) NULL;
-
-alter table ObjectCustomFieldValues add column ContentEncoding varchar(80) NULL;
-
# These could fail if there's no such index and there's no "drop index if exists" syntax
#alter table ObjectCustomFieldValues drop index ticketcustomfieldvalues1;
#alter table ObjectCustomFieldValues drop index ticketcustomfieldvalues2;
-alter table ObjectCustomFieldValues add index ObjectCustomFieldValues1 (Content);
-
-alter table ObjectCustomFieldValues add index ObjectCustomFieldValues2 (CustomField,ObjectType,ObjectId);
+alter table ObjectCustomFieldValues add index ObjectCustomFieldValues1 (Content),
+ add index ObjectCustomFieldValues2 (CustomField,ObjectType,ObjectId);
CREATE TABLE ObjectCustomFields (
@@ -50,10 +45,10 @@ CREATE TABLE ObjectCustomFields (
INSERT into ObjectCustomFields (id, CustomField, ObjectId, SortOrder, Creator, LastUpdatedBy) SELECT null, id, Queue, SortOrder, Creator, LastUpdatedBy from CustomFields;
-alter table CustomFields add column LookupType varchar(255) NOT NULL;
-alter table CustomFields add column Repeated int2 NOT NULL DEFAULT 0 ;
-alter table CustomFields add column Pattern varchar(255) NULL;
-alter table CustomFields add column MaxValues integer;
+alter table CustomFields add column LookupType varchar(255) NOT NULL,
+ add column Repeated int2 NOT NULL DEFAULT 0 ,
+ add column Pattern varchar(255) NULL,
+ add column MaxValues integer;
# See above
# alter table CustomFields drop index CustomFields1;
@@ -62,4 +57,4 @@ UPDATE CustomFields SET MaxValues = 1 WHERE Type LIKE '%Single';
UPDATE CustomFields SET Type = 'Select' WHERE Type LIKE 'Select%';
UPDATE CustomFields SET Type = 'Freeform' WHERE Type LIKE 'Freeform%';
UPDATE CustomFields Set LookupType = 'RT::Queue-RT::Ticket';
-alter table CustomFields drop column Queue;
+alter table CustomFields drop column Queue;
diff --git a/rt/etc/upgrade/3.3.11/schema.mysql b/rt/etc/upgrade/3.3.11/schema.mysql
index cc35d40f2..eff84783f 100644
--- a/rt/etc/upgrade/3.3.11/schema.mysql
+++ b/rt/etc/upgrade/3.3.11/schema.mysql
@@ -1,5 +1,5 @@
-ALTER TABLE ObjectCustomFieldValues ADD COLUMN SortOrder INTEGER NOT NULL DEFAULT 0;
-ALTER TABLE ObjectCustomFieldValues ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
+ALTER TABLE ObjectCustomFieldValues ADD COLUMN SortOrder INTEGER NOT NULL DEFAULT 0,
+ ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
UPDATE ObjectCustomFieldValues SET Disabled = 1 WHERE Current = 0;
ALTER TABLE ObjectCustomFieldValues DROP COLUMN Current;
diff --git a/rt/etc/upgrade/3.9.5/schema.mysql b/rt/etc/upgrade/3.9.5/schema.mysql
index 4bd0907c0..fe5018c78 100644
--- a/rt/etc/upgrade/3.9.5/schema.mysql
+++ b/rt/etc/upgrade/3.9.5/schema.mysql
@@ -6,15 +6,15 @@ AND CustomFieldValues.id = Attributes.ObjectId);
DELETE FROM Attributes WHERE Name = 'Category' AND ObjectType = 'RT::CustomFieldValue';
-ALTER TABLE Groups ADD COLUMN Creator integer NOT NULL DEFAULT 0;
-ALTER TABLE Groups ADD COLUMN Created DATETIME NULL;
-ALTER TABLE Groups ADD COLUMN LastUpdatedBy integer NOT NULL DEFAULT 0;
-ALTER TABLE Groups ADD COLUMN LastUpdated DATETIME NULL;
-ALTER TABLE GroupMembers ADD COLUMN Creator integer NOT NULL DEFAULT 0;
-ALTER TABLE GroupMembers ADD COLUMN Created DATETIME NULL;
-ALTER TABLE GroupMembers ADD COLUMN LastUpdatedBy integer NOT NULL DEFAULT 0;
-ALTER TABLE GroupMembers ADD COLUMN LastUpdated DATETIME NULL;
-ALTER TABLE ACL ADD COLUMN Creator integer NOT NULL DEFAULT 0;
-ALTER TABLE ACL ADD COLUMN Created DATETIME NULL;
-ALTER TABLE ACL ADD COLUMN LastUpdatedBy integer NOT NULL DEFAULT 0;
-ALTER TABLE ACL ADD COLUMN LastUpdated DATETIME NULL;
+ALTER TABLE Groups ADD COLUMN Creator integer NOT NULL DEFAULT 0,
+ ADD COLUMN Created DATETIME NULL,
+ ADD COLUMN LastUpdatedBy integer NOT NULL DEFAULT 0,
+ ADD COLUMN LastUpdated DATETIME NULL;
+ALTER TABLE GroupMembers ADD COLUMN Creator integer NOT NULL DEFAULT 0,
+ ADD COLUMN Created DATETIME NULL,
+ ADD COLUMN LastUpdatedBy integer NOT NULL DEFAULT 0,
+ ADD COLUMN LastUpdated DATETIME NULL;
+ALTER TABLE ACL ADD COLUMN Creator integer NOT NULL DEFAULT 0,
+ ADD COLUMN Created DATETIME NULL,
+ ADD COLUMN LastUpdatedBy integer NOT NULL DEFAULT 0,
+ ADD COLUMN LastUpdated DATETIME NULL;
diff --git a/rt/etc/upgrade/3.9.7/schema.mysql b/rt/etc/upgrade/3.9.7/schema.mysql
index 1be165647..4cbed6cc7 100644
--- a/rt/etc/upgrade/3.9.7/schema.mysql
+++ b/rt/etc/upgrade/3.9.7/schema.mysql
@@ -1,6 +1,6 @@
ALTER TABLE Users ADD COLUMN AuthToken VARCHAR(16) CHARACTER SET ascii NULL;
-ALTER TABLE CustomFields ADD COLUMN BasedOn INTEGER NULL;
-ALTER TABLE CustomFields ADD COLUMN RenderType VARCHAR(64) NULL;
-ALTER TABLE CustomFields ADD COLUMN ValuesClass VARCHAR(64) CHARACTER SET ascii NULL;
-ALTER TABLE Queues ADD COLUMN SubjectTag VARCHAR(120) NULL;
-ALTER TABLE Queues ADD COLUMN Lifecycle VARCHAR(32) NULL;
+ALTER TABLE CustomFields ADD COLUMN BasedOn INTEGER NULL,
+ ADD COLUMN RenderType VARCHAR(64) NULL,
+ ADD COLUMN ValuesClass VARCHAR(64) CHARACTER SET ascii NULL;
+ALTER TABLE Queues ADD COLUMN SubjectTag VARCHAR(120) NULL,
+ ADD COLUMN Lifecycle VARCHAR(32) NULL;
diff --git a/rt/lib/RT.pm b/rt/lib/RT.pm
index 063f7f719..4372a564d 100644
--- a/rt/lib/RT.pm
+++ b/rt/lib/RT.pm
@@ -138,6 +138,8 @@ up logging|/InitLogging>, and L<loads plugins|/InitPlugins>.
sub Init {
+ my @arg = @_;
+
CheckPerlRequirements();
InitPluginPaths();
@@ -146,7 +148,7 @@ sub Init {
ConnectToDatabase();
InitSystemObjects();
InitClasses();
- InitLogging();
+ InitLogging(@arg);
InitPlugins();
RT::I18N->Init;
RT->Config->PostLoadCheck;
@@ -174,6 +176,8 @@ Create the Logger object and set up signal handlers.
sub InitLogging {
+ my %arg = @_;
+
# We have to set the record separator ($, man perlvar)
# or Log::Dispatch starts getting
# really pissy, as some other module we use unsets it.
@@ -309,11 +313,14 @@ sub InitLogging {
));
}
}
- InitSignalHandlers();
+ InitSignalHandlers(%arg);
}
sub InitSignalHandlers {
+ my %arg = @_;
+ return if $arg{'NoSignalHandlers'};
+
# Signal handlers
## This is the default handling of warnings and die'ings in the code
## (including other used modules - maybe except for errors catched by
diff --git a/rt/lib/RT.pm.in b/rt/lib/RT.pm.in
deleted file mode 100644
index fafd2b778..000000000
--- a/rt/lib/RT.pm.in
+++ /dev/null
@@ -1,719 +0,0 @@
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
-# <sales@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
-use strict;
-use warnings;
-
-package RT;
-
-
-use File::Spec ();
-use Cwd ();
-
-use vars qw($Config $System $SystemUser $Nobody $Handle $Logger $_INSTALL_MODE);
-
-our $VERSION = '@RT_VERSION_MAJOR@.@RT_VERSION_MINOR@.@RT_VERSION_PATCH@';
-
-@DATABASE_ENV_PREF@
-
-our $BasePath = '@RT_PATH@';
-our $EtcPath = '@RT_ETC_PATH@';
-our $BinPath = '@RT_BIN_PATH@';
-our $SbinPath = '@RT_SBIN_PATH@';
-our $VarPath = '@RT_VAR_PATH@';
-our $PluginPath = '@RT_PLUGIN_PATH@';
-our $LocalPath = '@RT_LOCAL_PATH@';
-our $LocalEtcPath = '@LOCAL_ETC_PATH@';
-our $LocalLibPath = '@LOCAL_LIB_PATH@';
-our $LocalLexiconPath = '@LOCAL_LEXICON_PATH@';
-our $LocalPluginPath = $LocalPath."/plugins";
-
-
-# $MasonComponentRoot is where your rt instance keeps its mason html files
-
-our $MasonComponentRoot = '@MASON_HTML_PATH@';
-
-# $MasonLocalComponentRoot is where your rt instance keeps its site-local
-# mason html files.
-
-our $MasonLocalComponentRoot = '@MASON_LOCAL_HTML_PATH@';
-
-# $MasonDataDir Where mason keeps its datafiles
-
-our $MasonDataDir = '@MASON_DATA_PATH@';
-
-# RT needs to put session data (for preserving state between connections
-# via the web interface)
-our $MasonSessionDir = '@MASON_SESSION_PATH@';
-
-unless ( File::Spec->file_name_is_absolute($EtcPath) ) {
-
-# if BasePath exists and is absolute, we won't infer it from $INC{'RT.pm'}.
-# otherwise RT.pm will make src dir(where we configure RT) be the BasePath
-# instead of the --prefix one
- unless ( -d $BasePath && File::Spec->file_name_is_absolute($BasePath) ) {
- my $pm_path = ( File::Spec->splitpath( $INC{'RT.pm'} ) )[1];
-
- # need rel2abs here is to make sure path is absolute, since $INC{'RT.pm'}
- # is not always absolute
- $BasePath =
- File::Spec->rel2abs(
- File::Spec->catdir( $pm_path, File::Spec->updir ) );
- }
-
- $BasePath = Cwd::realpath( $BasePath );
-
- for my $path ( qw/EtcPath BinPath SbinPath VarPath LocalPath LocalEtcPath
- LocalLibPath LocalLexiconPath PluginPath LocalPluginPath
- MasonComponentRoot MasonLocalComponentRoot MasonDataDir
- MasonSessionDir/ ) {
- no strict 'refs';
- # just change relative ones
- $$path = File::Spec->catfile( $BasePath, $$path )
- unless File::Spec->file_name_is_absolute( $$path );
- }
-}
-
-
-=head1 NAME
-
-RT - Request Tracker
-
-=head1 SYNOPSIS
-
-A fully featured request tracker package
-
-=head1 DESCRIPTION
-
-=head2 INITIALIZATION
-
-=head2 LoadConfig
-
-Load RT's config file. First, the site configuration file
-(F<RT_SiteConfig.pm>) is loaded, in order to establish overall site
-settings like hostname and name of RT instance. Then, the core
-configuration file (F<RT_Config.pm>) is loaded to set fallback values
-for all settings; it bases some values on settings from the site
-configuration file.
-
-In order for the core configuration to not override the site's
-settings, the function C<Set> is used; it only sets values if they
-have not been set already.
-
-=cut
-
-sub LoadConfig {
- require RT::Config;
- $Config = new RT::Config;
- $Config->LoadConfigs;
- require RT::I18N;
-
- # RT::Essentials mistakenly recommends that WebPath be set to '/'.
- # If the user does that, do what they mean.
- $RT::WebPath = '' if ($RT::WebPath eq '/');
-
- # fix relative LogDir and GnuPG homedir
- unless ( File::Spec->file_name_is_absolute( $Config->Get('LogDir') ) ) {
- $Config->Set( LogDir =>
- File::Spec->catfile( $BasePath, $Config->Get('LogDir') ) );
- }
-
- my $gpgopts = $Config->Get('GnuPGOptions');
- unless ( File::Spec->file_name_is_absolute( $gpgopts->{homedir} ) ) {
- $gpgopts->{homedir} = File::Spec->catfile( $BasePath, $gpgopts->{homedir} );
- }
-
- RT::I18N->Init;
-}
-
-=head2 Init
-
-L<Connect to the database /ConnectToDatabase>, L<initilizes system objects /InitSystemObjects>,
-L<preloads classes /InitClasses> and L<set up logging /InitLogging>.
-
-=cut
-
-sub Init {
-
- my @arg = @_;
-
- CheckPerlRequirements();
-
- InitPluginPaths();
-
- #Get a database connection
- ConnectToDatabase();
- InitSystemObjects();
- InitClasses();
- InitLogging(@arg);
- InitPlugins();
- RT->Config->PostLoadCheck;
-
-}
-
-=head2 ConnectToDatabase
-
-Get a database connection. See also </Handle>.
-
-=cut
-
-sub ConnectToDatabase {
- require RT::Handle;
- $Handle = new RT::Handle unless $Handle;
- $Handle->Connect;
- return $Handle;
-}
-
-=head2 InitLogging
-
-Create the Logger object and set up signal handlers.
-
-=cut
-
-sub InitLogging {
-
- my %arg = @_;
-
- # We have to set the record separator ($, man perlvar)
- # or Log::Dispatch starts getting
- # really pissy, as some other module we use unsets it.
- $, = '';
- use Log::Dispatch 1.6;
-
- my %level_to_num = (
- map( { $_ => } 0..7 ),
- debug => 0,
- info => 1,
- notice => 2,
- warning => 3,
- error => 4, 'err' => 4,
- critical => 5, crit => 5,
- alert => 6,
- emergency => 7, emerg => 7,
- );
-
- unless ( $RT::Logger ) {
-
- $RT::Logger = Log::Dispatch->new;
-
- my $stack_from_level;
- if ( $stack_from_level = RT->Config->Get('LogStackTraces') ) {
- # if option has old style '\d'(true) value
- $stack_from_level = 0 if $stack_from_level =~ /^\d+$/;
- $stack_from_level = $level_to_num{ $stack_from_level } || 0;
- } else {
- $stack_from_level = 99; # don't log
- }
-
- my $simple_cb = sub {
- # if this code throw any warning we can get segfault
- no warnings;
- my %p = @_;
-
- # skip Log::* stack frames
- my $frame = 0;
- $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
- my ($package, $filename, $line) = caller($frame);
-
- $p{'message'} =~ s/(?:\r*\n)+$//;
- return "[". gmtime(time) ."] [". $p{'level'} ."]: "
- . $p{'message'} ." ($filename:$line)\n";
- };
-
- my $syslog_cb = sub {
- # if this code throw any warning we can get segfault
- no warnings;
- my %p = @_;
-
- my $frame = 0; # stack frame index
- # skip Log::* stack frames
- $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
- my ($package, $filename, $line) = caller($frame);
-
- # syswrite() cannot take utf8; turn it off here.
- Encode::_utf8_off($p{message});
-
- $p{message} =~ s/(?:\r*\n)+$//;
- if ($p{level} eq 'debug') {
- return "$p{message}\n";
- } else {
- return "$p{message} ($filename:$line)\n";
- }
- };
-
- my $stack_cb = sub {
- no warnings;
- my %p = @_;
- return $p{'message'} unless $level_to_num{ $p{'level'} } >= $stack_from_level;
-
- require Devel::StackTrace;
- my $trace = Devel::StackTrace->new( ignore_class => [ 'Log::Dispatch', 'Log::Dispatch::Base' ] );
- return $p{'message'} . $trace->as_string;
-
- # skip calling of the Log::* subroutins
- my $frame = 0;
- $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
- $frame++ while caller($frame) && (caller($frame))[3] =~ /^Log::/;
-
- $p{'message'} .= "\nStack trace:\n";
- while( my ($package, $filename, $line, $sub) = caller($frame++) ) {
- $p{'message'} .= "\t$sub(...) called at $filename:$line\n";
- }
- return $p{'message'};
- };
-
- if ( $Config->Get('LogToFile') ) {
- my ($filename, $logdir) = (
- $Config->Get('LogToFileNamed') || 'rt.log',
- $Config->Get('LogDir') || File::Spec->catdir( $VarPath, 'log' ),
- );
- if ( $filename =~ m![/\\]! ) { # looks like an absolute path.
- ($logdir) = $filename =~ m{^(.*[/\\])};
- }
- else {
- $filename = File::Spec->catfile( $logdir, $filename );
- }
-
- unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) {
- # localizing here would be hard when we don't have a current user yet
- die "Log file '$filename' couldn't be written or created.\n RT can't run.";
- }
-
- require Log::Dispatch::File;
- $RT::Logger->add( Log::Dispatch::File->new
- ( name=>'file',
- min_level=> $Config->Get('LogToFile'),
- filename=> $filename,
- mode=>'append',
- callbacks => [ $simple_cb, $stack_cb ],
- ));
- }
- if ( $Config->Get('LogToScreen') ) {
- require Log::Dispatch::Screen;
- $RT::Logger->add( Log::Dispatch::Screen->new
- ( name => 'screen',
- min_level => $Config->Get('LogToScreen'),
- callbacks => [ $simple_cb, $stack_cb ],
- stderr => 1,
- ));
- }
- if ( $Config->Get('LogToSyslog') ) {
- require Log::Dispatch::Syslog;
- $RT::Logger->add(Log::Dispatch::Syslog->new
- ( name => 'syslog',
- ident => 'RT',
- min_level => $Config->Get('LogToSyslog'),
- callbacks => [ $syslog_cb, $stack_cb ],
- stderr => 1,
- $Config->Get('LogToSyslogConf'),
- ));
- }
- }
- InitSignalHandlers(%arg);
-}
-
-sub InitSignalHandlers {
-
- my %arg = @_;
-
-# Signal handlers
-## This is the default handling of warnings and die'ings in the code
-## (including other used modules - maybe except for errors catched by
-## Mason). It will log all problems through the standard logging
-## mechanism (see above).
-
- unless ( $arg{'NoSignalHandlers'} ) {
-
- $SIG{__WARN__} = sub {
- # The 'wide character' warnings has to be silenced for now, at least
- # until HTML::Mason offers a sane way to process both raw output and
- # unicode strings.
- # use 'goto &foo' syntax to hide ANON sub from stack
- if( index($_[0], 'Wide character in ') != 0 ) {
- unshift @_, $RT::Logger, qw(level warning message);
- goto &Log::Dispatch::log;
- }
- };
-
- #When we call die, trap it and log->crit with the value of the die.
-
- $SIG{__DIE__} = sub {
- # if we are not in eval and perl is not parsing code
- # then rollback transactions and log RT error
- unless ($^S || !defined $^S ) {
- $RT::Handle->Rollback(1) if $RT::Handle;
- $RT::Logger->crit("$_[0]") if $RT::Logger;
- }
- die $_[0];
- };
-
- }
-}
-
-
-sub CheckPerlRequirements {
- if ($^V < 5.008003) {
- die sprintf "RT requires Perl v5.8.3 or newer. Your current Perl is v%vd\n", $^V;
- }
-
- # use $error here so the following "die" can still affect the global $@
- my $error;
- {
- local $@;
- eval {
- my $x = '';
- my $y = \$x;
- require Scalar::Util;
- Scalar::Util::weaken($y);
- };
- $error = $@;
- }
-
- if ($error) {
- die <<"EOF";
-
-RT requires the Scalar::Util module be built with support for the 'weaken'
-function.
-
-It is sometimes the case that operating system upgrades will replace
-a working Scalar::Util with a non-working one. If your system was working
-correctly up until now, this is likely the cause of the problem.
-
-Please reinstall Scalar::Util, being careful to let it build with your C
-compiler. Ususally this is as simple as running the following command as
-root.
-
- perl -MCPAN -e'install Scalar::Util'
-
-EOF
-
- }
-}
-
-=head2 InitClasses
-
-Load all modules that define base classes.
-
-=cut
-
-sub InitClasses {
- shift if @_%2; # so we can call it as a function or method
- my %args = (@_);
- require RT::Tickets;
- require RT::Transactions;
- require RT::Attachments;
- require RT::Users;
- require RT::Principals;
- require RT::CurrentUser;
- require RT::Templates;
- require RT::Queues;
- require RT::ScripActions;
- require RT::ScripConditions;
- require RT::Scrips;
- require RT::Groups;
- require RT::GroupMembers;
- require RT::CustomFields;
- require RT::CustomFieldValues;
- require RT::ObjectCustomFields;
- require RT::ObjectCustomFieldValues;
- require RT::Attributes;
- require RT::Dashboard;
- require RT::Approval;
-
- # on a cold server (just after restart) people could have an object
- # in the session, as we deserialize it so we never call constructor
- # of the class, so the list of accessible fields is empty and we die
- # with "Method xxx is not implemented in RT::SomeClass"
- $_->_BuildTableAttributes foreach qw(
- RT::Ticket
- RT::Transaction
- RT::Attachment
- RT::User
- RT::Principal
- RT::Template
- RT::Queue
- RT::ScripAction
- RT::ScripCondition
- RT::Scrip
- RT::Group
- RT::GroupMember
- RT::CustomField
- RT::CustomFieldValue
- RT::ObjectCustomField
- RT::ObjectCustomFieldValue
- RT::Attribute
- );
-
- if ( $args{'Heavy'} ) {
- # load scrips' modules
- my $scrips = RT::Scrips->new($RT::SystemUser);
- $scrips->Limit( FIELD => 'Stage', OPERATOR => '!=', VALUE => 'Disabled' );
- while ( my $scrip = $scrips->Next ) {
- local $@;
- eval { $scrip->LoadModules } or
- $RT::Logger->error("Invalid Scrip ".$scrip->Id.". Unable to load the Action or Condition. ".
- "You should delete or repair this Scrip in the admin UI.\n$@\n");
- }
-
- foreach my $class ( grep $_, RT->Config->Get('CustomFieldValuesSources') ) {
- local $@;
- eval "require $class; 1" or $RT::Logger->error(
- "Class '$class' is listed in CustomFieldValuesSources option"
- ." in the config, but we failed to load it:\n$@\n"
- );
- }
-
- RT::I18N->LoadLexicons;
- }
-}
-
-=head2 InitSystemObjects
-
-Initializes system objects: C<$RT::System>, C<$RT::SystemUser>
-and C<$RT::Nobody>.
-
-=cut
-
-sub InitSystemObjects {
-
- #RT's system user is a genuine database user. its id lives here
- require RT::CurrentUser;
- $SystemUser = new RT::CurrentUser;
- $SystemUser->LoadByName('RT_System');
-
- #RT's "nobody user" is a genuine database user. its ID lives here.
- $Nobody = new RT::CurrentUser;
- $Nobody->LoadByName('Nobody');
-
- require RT::System;
- $System = RT::System->new( $SystemUser );
-}
-
-=head1 CLASS METHODS
-
-=head2 Config
-
-Returns the current L<config object RT::Config>, but note that
-you must L<load config /LoadConfig> first otherwise this method
-returns undef.
-
-Method can be called as class method.
-
-=cut
-
-sub Config { return $Config }
-
-=head2 DatabaseHandle
-
-Returns the current L<database handle object RT::Handle>.
-
-See also L</ConnectToDatabase>.
-
-=cut
-
-sub DatabaseHandle { return $Handle }
-
-=head2 Logger
-
-Returns the logger. See also L</InitLogging>.
-
-=cut
-
-sub Logger { return $Logger }
-
-=head2 System
-
-Returns the current L<system object RT::System>. See also
-L</InitSystemObjects>.
-
-=cut
-
-sub System { return $System }
-
-=head2 SystemUser
-
-Returns the system user's object, it's object of
-L<RT::CurrentUser> class that represents the system. See also
-L</InitSystemObjects>.
-
-=cut
-
-sub SystemUser { return $SystemUser }
-
-=head2 Nobody
-
-Returns object of Nobody. It's object of L<RT::CurrentUser> class
-that represents a user who can own ticket and nothing else. See
-also L</InitSystemObjects>.
-
-=cut
-
-sub Nobody { return $Nobody }
-
-=head2 Plugins
-
-Returns a listref of all Plugins currently configured for this RT instance.
-You can define plugins by adding them to the @Plugins list in your RT_SiteConfig
-
-=cut
-
-our @PLUGINS = ();
-sub Plugins {
- my $self = shift;
- unless (@PLUGINS) {
- $self->InitPluginPaths;
- @PLUGINS = $self->InitPlugins;
- }
- return \@PLUGINS;
-}
-
-=head2 PluginDirs
-
-Takes optional subdir (e.g. po, lib, etc.) and return plugins' dirs that exist.
-
-This code chacke plugins names or anything else and required when main config
-is loaded to load plugins' configs.
-
-=cut
-
-sub PluginDirs {
- my $self = shift;
- my $subdir = shift;
-
- require RT::Plugin;
-
- my @res;
- foreach my $plugin (grep $_, RT->Config->Get('Plugins')) {
- my $path = RT::Plugin->new( name => $plugin )->Path( $subdir );
- next unless -d $path;
- push @res, $path;
- }
- return @res;
-}
-
-=head2 InitPluginPaths
-
-Push plugins' lib paths into @INC right after F<local/lib>.
-In case F<local/lib> isn't in @INC, append them to @INC
-
-=cut
-
-sub InitPluginPaths {
- my $self = shift || __PACKAGE__;
-
- my @lib_dirs = $self->PluginDirs('lib');
-
- my @tmp_inc;
- my $added;
- for (@INC) {
- if ( Cwd::realpath($_) eq $RT::LocalLibPath) {
- push @tmp_inc, $_, @lib_dirs;
- $added = 1;
- } else {
- push @tmp_inc, $_;
- }
- }
-
- # append @lib_dirs in case $RT::LocalLibPath isn't in @INC
- push @tmp_inc, @lib_dirs unless $added;
-
- my %seen;
- @INC = grep !$seen{$_}++, @tmp_inc;
-}
-
-=head2 InitPlugins
-
-Initialze all Plugins found in the RT configuration file, setting up their lib and HTML::Mason component roots.
-
-=cut
-
-sub InitPlugins {
- my $self = shift;
- my @plugins;
- require RT::Plugin;
- foreach my $plugin (grep $_, RT->Config->Get('Plugins')) {
- $plugin->require;
- die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
- push @plugins, RT::Plugin->new(name =>$plugin);
- }
- return @plugins;
-}
-
-
-sub InstallMode {
- my $self = shift;
- if (@_) {
- $_INSTALL_MODE = shift;
- if($_INSTALL_MODE) {
- require RT::CurrentUser;
- $SystemUser = RT::CurrentUser->new();
- }
- }
- return $_INSTALL_MODE;
-}
-
-
-=head1 BUGS
-
-Please report them to rt-bugs@bestpractical.com, if you know what's
-broken and have at least some idea of what needs to be fixed.
-
-If you're not sure what's going on, report them rt-devel@lists.bestpractical.com.
-
-=head1 SEE ALSO
-
-L<RT::StyleGuide>
-L<DBIx::SearchBuilder>
-
-
-=cut
-
-require RT::Base;
-RT::Base->_ImportOverlays();
-
-1;
diff --git a/rt/lib/RT/Action/CreateTickets.pm b/rt/lib/RT/Action/CreateTickets.pm
index 31489c8ff..efd2bdaf6 100644
--- a/rt/lib/RT/Action/CreateTickets.pm
+++ b/rt/lib/RT/Action/CreateTickets.pm
@@ -567,7 +567,8 @@ sub Parse {
$self->_ParseMultilineTemplate(%args);
} elsif ( $args{'Content'} =~ /(?:\t|,)/i ) {
$self->_ParseXSVTemplate(%args);
-
+ } else {
+ RT->Logger->error("Invalid Template Content (Couldn't find ===, and is not a csv/tsv template) - unable to parse: $args{Content}");
}
}
diff --git a/rt/lib/RT/Articles.pm b/rt/lib/RT/Articles.pm
index 8dd661d2e..47d0ebea2 100644
--- a/rt/lib/RT/Articles.pm
+++ b/rt/lib/RT/Articles.pm
@@ -360,6 +360,7 @@ sub LimitCustomField {
QUOTEVALUE => $args{'QUOTEVALUE'},
ENTRYAGGREGATOR => 'AND', #$args{'ENTRYAGGREGATOR'},
SUBCLAUSE => $clause,
+ CASESENSITIVE => 0,
);
$self->SUPER::Limit(
ALIAS => $ObjectValuesAlias,
@@ -380,6 +381,7 @@ sub LimitCustomField {
QUOTEVALUE => $args{'QUOTEVALUE'},
ENTRYAGGREGATOR => $args{'ENTRYAGGREGATOR'},
SUBCLAUSE => $clause,
+ CASESENSITIVE => 0,
);
$self->SUPER::Limit(
ALIAS => $ObjectValuesAlias,
@@ -389,6 +391,7 @@ sub LimitCustomField {
QUOTEVALUE => $args{'QUOTEVALUE'},
ENTRYAGGREGATOR => $args{'ENTRYAGGREGATOR'},
SUBCLAUSE => $clause,
+ CASESENSITIVE => 0,
);
}
}
diff --git a/rt/lib/RT/Config.pm b/rt/lib/RT/Config.pm
index f87ef84c9..014c76468 100644
--- a/rt/lib/RT/Config.pm
+++ b/rt/lib/RT/Config.pm
@@ -411,8 +411,8 @@ our %META = (
Description => q|What tickets to display in the 'More about requestor' box|, #loc
Values => [qw(Active Inactive All None)],
ValuesLabel => {
- Active => "Show the Requestor's 10 highest priority open tickets", #loc
- Inactive => "Show the Requestor's 10 highest priority closed tickets", #loc
+ Active => "Show the Requestor's 10 highest priority active tickets", #loc
+ Inactive => "Show the Requestor's 10 highest priority inactive tickets", #loc
All => "Show the Requestor's 10 highest priority tickets", #loc
None => "Show no tickets for the Requestor", #loc
},
@@ -749,7 +749,7 @@ our %META = (
my %seen;
foreach my $encoding ( grep defined && length, splice @$value ) {
- next if $seen{ $encoding }++;
+ next if $seen{ $encoding };
if ( $encoding eq '*' ) {
unshift @$value, '*';
next;
diff --git a/rt/lib/RT/Crypt/GnuPG.pm b/rt/lib/RT/Crypt/GnuPG.pm
index ab444d068..c5fb12bef 100644
--- a/rt/lib/RT/Crypt/GnuPG.pm
+++ b/rt/lib/RT/Crypt/GnuPG.pm
@@ -1683,6 +1683,7 @@ my %ignore_keyword = map { $_ => 1 } qw(
BEGIN_ENCRYPTION SIG_ID VALIDSIG
ENC_TO BEGIN_DECRYPTION END_DECRYPTION GOODMDC
TRUST_UNDEFINED TRUST_NEVER TRUST_MARGINAL TRUST_FULLY TRUST_ULTIMATE
+ DECRYPTION_INFO
);
sub ParseStatus {
diff --git a/rt/lib/RT/Dashboard.pm b/rt/lib/RT/Dashboard.pm
index 14ffa6ad3..2e2bbc489 100644
--- a/rt/lib/RT/Dashboard.pm
+++ b/rt/lib/RT/Dashboard.pm
@@ -454,6 +454,36 @@ sub CurrentUserCanCreateAny {
return 0;
}
+=head2 Delete
+
+Deletes the dashboard and related subscriptions.
+Returns a tuple of status and message, where status is true upon success.
+
+=cut
+
+sub Delete {
+ my $self = shift;
+ my $id = $self->id;
+ my ( $status, $msg ) = $self->SUPER::Delete(@_);
+ if ( $status ) {
+ # delete all the subscriptions
+ my $subscriptions = RT::Attributes->new( RT->SystemUser );
+ $subscriptions->Limit(
+ FIELD => 'Name',
+ VALUE => 'Subscription',
+ );
+ $subscriptions->Limit(
+ FIELD => 'Description',
+ VALUE => "Subscription to dashboard $id",
+ );
+ while ( my $subscription = $subscriptions->Next ) {
+ $subscription->Delete();
+ }
+ }
+
+ return ( $status, $msg );
+}
+
RT::Base->_ImportOverlays();
1;
diff --git a/rt/lib/RT/Generated.pm b/rt/lib/RT/Generated.pm
index 2abcf3b6e..9fd946f5b 100644
--- a/rt/lib/RT/Generated.pm
+++ b/rt/lib/RT/Generated.pm
@@ -50,7 +50,7 @@ package RT;
use warnings;
use strict;
-our $VERSION = '4.0.6';
+our $VERSION = '4.0.7';
diff --git a/rt/lib/RT/I18N.pm b/rt/lib/RT/I18N.pm
index cadf7cc7c..e453cfa04 100644
--- a/rt/lib/RT/I18N.pm
+++ b/rt/lib/RT/I18N.pm
@@ -227,7 +227,7 @@ sub SetMIMEEntityToEncoding {
my $body = $entity->bodyhandle;
- if ( $enc ne $charset && $body ) {
+ if ( $body && ($enc ne $charset || $enc =~ /^utf-?8(?:-strict)?$/i) ) {
my $string = $body->as_string or return;
$RT::Logger->debug( "Converting '$charset' to '$enc' for "
@@ -335,7 +335,7 @@ sub DecodeMIMEWordsToEncoding {
}
# now we have got a decoded subject, try to convert into the encoding
- unless ( $charset eq $to_charset ) {
+ if ( $charset ne $to_charset || $charset =~ /^utf-?8(?:-strict)?$/i ) {
Encode::from_to( $enc_str, $charset, $to_charset );
}
@@ -537,7 +537,7 @@ sub SetMIMEHeadToEncoding {
my @values = $head->get_all($tag);
$head->delete($tag);
foreach my $value (@values) {
- if ( $charset ne $enc ) {
+ if ( $charset ne $enc || $enc =~ /^utf-?8(?:-strict)?$/i ) {
Encode::_utf8_off($value);
Encode::from_to( $value, $charset => $enc );
}
diff --git a/rt/lib/RT/Interface/Email.pm b/rt/lib/RT/Interface/Email.pm
index 02a1ec0c0..4c3ee9986 100755
--- a/rt/lib/RT/Interface/Email.pm
+++ b/rt/lib/RT/Interface/Email.pm
@@ -787,7 +787,7 @@ sub GetForwardFrom {
my $ticket = $args{Ticket} || $txn->Object;
if ( RT->Config->Get('ForwardFromUser') ) {
- return ( $txn || $ticket )->CurrentUser->UserObj->EmailAddress;
+ return ( $txn || $ticket )->CurrentUser->EmailAddress;
}
else {
return $ticket->QueueObj->CorrespondAddress
@@ -1221,8 +1221,16 @@ sub SetInReplyTo {
if @references > 10;
my $mail = $args{'Message'};
- $mail->head->set( 'In-Reply-To' => join ' ', @rtid? (@rtid) : (@id) ) if @id || @rtid;
- $mail->head->set( 'References' => join ' ', @references );
+ $mail->head->set( 'In-Reply-To' => Encode::encode_utf8(join ' ', @rtid? (@rtid) : (@id)) ) if @id || @rtid;
+ $mail->head->set( 'References' => Encode::encode_utf8(join ' ', @references) );
+}
+
+sub ExtractTicketId {
+ my $entity = shift;
+
+ my $subject = $entity->head->get('Subject') || '';
+ chomp $subject;
+ return ParseTicketId( $subject );
}
sub ParseTicketId {
@@ -1448,7 +1456,7 @@ sub Gateway {
}
# }}}
- $args{'ticket'} ||= ParseTicketId( $Subject );
+ $args{'ticket'} ||= ExtractTicketId( $Message );
$SystemTicket = RT::Ticket->new( RT->SystemUser );
$SystemTicket->Load( $args{'ticket'} ) if ( $args{'ticket'} ) ;
@@ -1704,17 +1712,20 @@ sub _RunUnsafeAction {
return ( 0, "Ticket not taken" );
}
} elsif ( $args{'Action'} =~ /^resolve$/i ) {
- my ( $status, $msg ) = $args{'Ticket'}->SetStatus('resolved');
- unless ($status) {
+ my $new_status = $args{'Ticket'}->FirstInactiveStatus;
+ if ($new_status) {
+ my ( $status, $msg ) = $args{'Ticket'}->SetStatus($new_status);
+ unless ($status) {
- #Warn the sender that we couldn't actually submit the comment.
- MailError(
- To => $args{'ErrorsTo'},
- Subject => "Ticket not resolved",
- Explanation => $msg,
- MIMEObj => $args{'Message'}
- );
- return ( 0, "Ticket not resolved" );
+ #Warn the sender that we couldn't actually submit the comment.
+ MailError(
+ To => $args{'ErrorsTo'},
+ Subject => "Ticket not resolved",
+ Explanation => $msg,
+ MIMEObj => $args{'Message'}
+ );
+ return ( 0, "Ticket not resolved" );
+ }
}
} else {
return ( 0, "Not supported unsafe action $args{'Action'}", $args{'Ticket'} );
diff --git a/rt/lib/RT/Interface/Web.pm b/rt/lib/RT/Interface/Web.pm
index 94da3072d..1aae7581e 100644
--- a/rt/lib/RT/Interface/Web.pm
+++ b/rt/lib/RT/Interface/Web.pm
@@ -261,7 +261,15 @@ sub HandleRequest {
$HTML::Mason::Commands::m->comp( '/Elements/SetupSessionCookie', %$ARGS );
SendSessionCookie();
- $HTML::Mason::Commands::session{'CurrentUser'} = RT::CurrentUser->new() unless _UserLoggedIn();
+
+ if ( _UserLoggedIn() ) {
+ # make user info up to date
+ $HTML::Mason::Commands::session{'CurrentUser'}
+ ->Load( $HTML::Mason::Commands::session{'CurrentUser'}->id );
+ }
+ else {
+ $HTML::Mason::Commands::session{'CurrentUser'} = RT::CurrentUser->new();
+ }
# Process session-related callbacks before any auth attempts
$HTML::Mason::Commands::m->callback( %$ARGS, CallbackName => 'Session', CallbackPage => '/autohandler' );
@@ -287,7 +295,7 @@ sub HandleRequest {
my $m = $HTML::Mason::Commands::m;
# REST urls get a special 401 response
- if ($m->request_comp->path =~ '^/REST/\d+\.\d+/') {
+ if ($m->request_comp->path =~ m{^/REST/\d+\.\d+/}) {
$HTML::Mason::Commands::r->content_type("text/plain");
$m->error_format("text");
$m->out("RT/$RT::VERSION 401 Credentials required\n");
@@ -457,7 +465,7 @@ sub MaybeShowInstallModePage {
my $m = $HTML::Mason::Commands::m;
if ( $m->base_comp->path =~ RT->Config->Get('WebNoAuthRegex') ) {
$m->call_next();
- } elsif ( $m->request_comp->path !~ '^(/+)Install/' ) {
+ } elsif ( $m->request_comp->path !~ m{^(/+)Install/} ) {
RT::Interface::Web::Redirect( RT->Config->Get('WebURL') . "Install/index.html" );
} else {
$m->call_next();
@@ -557,7 +565,7 @@ sub ShowRequestedPage {
unless ( $HTML::Mason::Commands::session{'CurrentUser'}->Privileged ) {
# if the user is trying to access a ticket, redirect them
- if ( $m->request_comp->path =~ '^(/+)Ticket/Display.html' && $ARGS->{'id'} ) {
+ if ( $m->request_comp->path =~ m{^(/+)Ticket/Display.html} && $ARGS->{'id'} ) {
RT::Interface::Web::Redirect( RT->Config->Get('WebURL') . "SelfService/Display.html?id=" . $ARGS->{'id'} );
}
@@ -659,7 +667,7 @@ sub AttemptExternalAuth {
delete $HTML::Mason::Commands::session{'CurrentUser'};
$user = $orig_user;
- if ( RT->Config->Get('WebExternalOnly') ) {
+ unless ( RT->Config->Get('WebFallbackToInternalAuth') ) {
TangentForLoginWithError('You are not an authorized user');
}
}
@@ -970,7 +978,7 @@ sub MobileClient {
my $self = shift;
-if (($ENV{'HTTP_USER_AGENT'} || '') =~ /(?:hiptop|Blazer|Novarra|Vagabond|SonyEricsson|Symbian|NetFront|UP.Browser|UP.Link|Windows CE|MIDP|J2ME|DoCoMo|J-PHONE|PalmOS|PalmSource|iPhone|iPod|AvantGo|Nokia|Android|WebOS|S60)/io && !$HTML::Mason::Commands::session{'NotMobile'}) {
+if (($ENV{'HTTP_USER_AGENT'} || '') =~ /(?:hiptop|Blazer|Novarra|Vagabond|SonyEricsson|Symbian|NetFront|UP.Browser|UP.Link|Windows CE|MIDP|J2ME|DoCoMo|J-PHONE|PalmOS|PalmSource|iPhone|iPod|AvantGo|Nokia|Android|WebOS|S60|Mobile)/io && !$HTML::Mason::Commands::session{'NotMobile'}) {
return 1;
} else {
return undef;
@@ -1183,6 +1191,14 @@ our %is_whitelisted_component = (
# information for the search. Because it's a straight-up read, in
# addition to embedding its own auth, it's fine.
'/NoAuth/rss/dhandler' => 1,
+
+ # While these can be used for denial-of-service against RT
+ # (construct a very inefficient query and trick lots of users into
+ # running them against RT) it's incredibly useful to be able to link
+ # to a search result or bookmark a result page.
+ '/Search/Results.html' => 1,
+ '/Search/Simple.html' => 1,
+ '/m/tickets/search' => 1,
);
sub IsCompCSRFWhitelisted {
@@ -1237,7 +1253,19 @@ sub IsRefererCSRFWhitelisted {
my $configs;
for my $config ( $base_url, RT->Config->Get('ReferrerWhitelist') ) {
push @$configs,$config;
- return 1 if $referer->host_port eq $config;
+
+ my $host_port = $referer->host_port;
+ if ($config =~ /\*/) {
+ # Turn a literal * into a domain component or partial component match.
+ # Refer to http://tools.ietf.org/html/rfc2818#page-5
+ my $regex = join "[a-zA-Z0-9\-]*",
+ map { quotemeta($_) }
+ split /\*/, $config;
+
+ return 1 if $host_port =~ /^$regex$/i;
+ } else {
+ return 1 if $host_port eq $config;
+ }
}
return (0,$referer,$configs);
@@ -1962,7 +1990,7 @@ sub MakeMIMEEntity {
);
my $Message = MIME::Entity->build(
Type => 'multipart/mixed',
- "Message-Id" => RT::Interface::Email::GenMessageId,
+ "Message-Id" => Encode::encode_utf8( RT::Interface::Email::GenMessageId ),
map { $_ => Encode::encode_utf8( $args{ $_} ) }
grep defined $args{$_}, qw(Subject From Cc)
);
diff --git a/rt/lib/RT/Record.pm b/rt/lib/RT/Record.pm
index e134178be..fd238de16 100755
--- a/rt/lib/RT/Record.pm
+++ b/rt/lib/RT/Record.pm
@@ -639,6 +639,8 @@ sub __Value {
my $value = $self->SUPER::__Value($field);
+ return undef if (!defined $value);
+
if ( $args{'decode_utf8'} ) {
if ( !utf8::is_utf8($value) ) {
utf8::decode($value);
@@ -1675,7 +1677,7 @@ sub _AddCustomFieldValue {
0,
$self->loc(
"Custom field [_1] does not apply to this object",
- $args{'Field'}
+ ref $args{'Field'} ? $args{'Field'}->id : $args{'Field'}
)
);
}
diff --git a/rt/lib/RT/Scrip.pm b/rt/lib/RT/Scrip.pm
index 950661624..8f97e747f 100755
--- a/rt/lib/RT/Scrip.pm
+++ b/rt/lib/RT/Scrip.pm
@@ -545,7 +545,7 @@ sub _Set {
}
}
- return $self->__Set(@_);
+ return $self->SUPER::_Set(@_);
}
diff --git a/rt/lib/RT/Scrips.pm b/rt/lib/RT/Scrips.pm
index 13a4b7d7d..fa33f7ec7 100755
--- a/rt/lib/RT/Scrips.pm
+++ b/rt/lib/RT/Scrips.pm
@@ -178,16 +178,6 @@ Commit all of this object's prepared scrips
sub Commit {
my $self = shift;
- # RT::Scrips->_SetupSourceObjects will clobber
- # the CurrentUser, but we need to keep this ticket
- # so that the _TransactionBatch cache is maintained
- # and doesn't run twice. sigh.
- $self->_StashCurrentUser( TicketObj => $self->{TicketObj} ) if $self->{TicketObj};
-
- #We're really going to need a non-acled ticket for the scrips to work
- $self->_SetupSourceObjects( TicketObj => $self->{'TicketObj'},
- TransactionObj => $self->{'TransactionObj'} );
-
foreach my $scrip (@{$self->Prepared}) {
$RT::Logger->debug(
"Committing scrip #". $scrip->id
@@ -199,8 +189,6 @@ sub Commit {
TransactionObj => $self->{'TransactionObj'} );
}
- # Apply the bandaid.
- $self->_RestoreCurrentUser( TicketObj => $self->{TicketObj} ) if $self->{TicketObj};
}
@@ -221,12 +209,6 @@ sub Prepare {
Type => undef,
@_ );
- # RT::Scrips->_SetupSourceObjects will clobber
- # the CurrentUser, but we need to keep this ticket
- # so that the _TransactionBatch cache is maintained
- # and doesn't run twice. sigh.
- $self->_StashCurrentUser( TicketObj => $args{TicketObj} ) if $args{TicketObj};
-
#We're really going to need a non-acled ticket for the scrips to work
$self->_SetupSourceObjects( TicketObj => $args{'TicketObj'},
Ticket => $args{'Ticket'},
@@ -259,10 +241,6 @@ sub Prepare {
}
- # Apply the bandaid.
- $self->_RestoreCurrentUser( TicketObj => $args{TicketObj} ) if $args{TicketObj};
-
-
return (@{$self->Prepared});
};
@@ -279,40 +257,6 @@ sub Prepared {
return ($self->{'prepared_scrips'} || []);
}
-=head2 _StashCurrentUser TicketObj => RT::Ticket
-
-Saves aside the current user of the original ticket that was passed to these scrips.
-This is used to make sure that we don't accidentally leak the RT_System current user
-back to the calling code.
-
-=cut
-
-sub _StashCurrentUser {
- my $self = shift;
- my %args = @_;
-
- $self->{_TicketCurrentUser} = $args{TicketObj}->CurrentUser;
-}
-
-=head2 _RestoreCurrentUser TicketObj => RT::Ticket
-
-Uses the current user saved by _StashCurrentUser to reset a Ticket object
-back to the caller's current user and avoid leaking an RT_System ticket to
-calling code.
-
-=cut
-
-sub _RestoreCurrentUser {
- my $self = shift;
- my %args = @_;
- unless ( $self->{_TicketCurrentUser} ) {
- RT->Logger->debug("Called _RestoreCurrentUser without a stashed current user object");
- return;
- }
- $args{TicketObj}->CurrentUser($self->{_TicketCurrentUser});
-
-}
-
=head2 _SetupSourceObjects { TicketObj , Ticket, Transaction, TransactionObj }
Setup a ticket and transaction for this Scrip collection to work with as it runs through the
@@ -334,14 +278,22 @@ sub _SetupSourceObjects {
@_ );
- if ( $self->{'TicketObj'} = $args{'TicketObj'} ) {
- # This clobbers the passed in TicketObj by turning it into one
- # whose current user is RT_System. Anywhere in the Web UI
- # currently calling into this is thus susceptable to a privilege
- # leak; the only current call site is ->Apply, which bandaids
- # over the top of this by re-asserting the CurrentUser
- # afterwards.
- $self->{'TicketObj'}->CurrentUser( $self->CurrentUser );
+ if ( $args{'TicketObj'} ) {
+ # This loads a clean copy of the Ticket object to ensure that we
+ # don't accidentally escalate the privileges of the passed in
+ # ticket (this function can be invoked from the UI).
+ # We copy the TransactionBatch transactions so that Scrips
+ # running against the new Ticket will have access to them. We
+ # use RanTransactionBatch to guard against running
+ # TransactionBatch Scrips more than once.
+ $self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser );
+ $self->{'TicketObj'}->Load( $args{'TicketObj'}->Id );
+ if ( $args{'TicketObj'}->TransactionBatch ) {
+ # try to ensure that we won't infinite loop if something dies, triggering DESTROY while
+ # we have the _TransactionBatch objects;
+ $self->{'TicketObj'}->RanTransactionBatch(1);
+ $self->{'TicketObj'}->{'_TransactionBatch'} = $args{'TicketObj'}->{'_TransactionBatch'};
+ }
}
else {
$self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser );
diff --git a/rt/lib/RT/Search/Googleish.pm b/rt/lib/RT/Search/Googleish.pm
index a1254836a..1b4071f4d 100644
--- a/rt/lib/RT/Search/Googleish.pm
+++ b/rt/lib/RT/Search/Googleish.pm
@@ -110,7 +110,7 @@ sub QueryToSQL {
(\w+) # A straight word
(?:\. # With an optional .foo
($RE{delimited}{-delim=>q['"]}
- |\w+
+ |[\w-]+ # Allow \w + dashes
) # Which could be ."foo bar", too
)?
)
@@ -225,6 +225,11 @@ sub GuessType {
return "default";
}
+# $_[0] is $self
+# $_[1] is escaped value without surrounding single quotes
+# $_[2] is a boolean of "was quoted by the user?"
+# ensure this is false before you do smart matching like $_[1] eq "me"
+# $_[3] is escaped subkey, if any (see HandleCf)
sub HandleDefault { return subject => "Subject LIKE '$_[1]'"; }
sub HandleSubject { return subject => "Subject LIKE '$_[1]'"; }
sub HandleFulltext { return content => "Content LIKE '$_[1]'"; }
@@ -242,7 +247,14 @@ sub HandleStatus {
}
}
sub HandleOwner {
- return owner => (!$_[2] and $_[1] eq "me") ? "Owner.id = '__CurrentUser__'" : "Owner = '$_[1]'";
+ if (!$_[2] and $_[1] eq "me") {
+ return owner => "Owner.id = '__CurrentUser__'";
+ }
+ elsif (!$_[2] and $_[1] =~ /\w+@\w+/) {
+ return owner => "Owner.EmailAddress = '$_[1]'";
+ } else {
+ return owner => "Owner = '$_[1]'";
+ }
}
sub HandleWatcher {
return watcher => (!$_[2] and $_[1] eq "me") ? "Watcher.id = '__CurrentUser__'" : "Watcher = '$_[1]'";
diff --git a/rt/lib/RT/SearchBuilder.pm b/rt/lib/RT/SearchBuilder.pm
index 3e9855110..4278f7587 100644
--- a/rt/lib/RT/SearchBuilder.pm
+++ b/rt/lib/RT/SearchBuilder.pm
@@ -211,29 +211,35 @@ sub LimitCustomField {
@_ );
my $alias = $self->Join(
- TYPE => 'left',
- ALIAS1 => 'main',
- FIELD1 => 'id',
- TABLE2 => 'ObjectCustomFieldValues',
- FIELD2 => 'ObjectId'
+ TYPE => 'left',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'ObjectCustomFieldValues',
+ FIELD2 => 'ObjectId'
);
$self->Limit(
- ALIAS => $alias,
- FIELD => 'CustomField',
- OPERATOR => '=',
- VALUE => $args{'CUSTOMFIELD'},
+ ALIAS => $alias,
+ FIELD => 'CustomField',
+ OPERATOR => '=',
+ VALUE => $args{'CUSTOMFIELD'},
) if ($args{'CUSTOMFIELD'});
$self->Limit(
- ALIAS => $alias,
- FIELD => 'ObjectType',
- OPERATOR => '=',
- VALUE => $self->_SingularClass,
+ ALIAS => $alias,
+ FIELD => 'ObjectType',
+ OPERATOR => '=',
+ VALUE => $self->_SingularClass,
);
$self->Limit(
- ALIAS => $alias,
- FIELD => 'Content',
- OPERATOR => $args{'OPERATOR'},
- VALUE => $args{'VALUE'},
+ ALIAS => $alias,
+ FIELD => 'Content',
+ OPERATOR => $args{'OPERATOR'},
+ VALUE => $args{'VALUE'},
+ );
+ $self->Limit(
+ ALIAS => $alias,
+ FIELD => 'Disabled',
+ OPERATOR => '=',
+ VALUE => 0,
);
}
diff --git a/rt/lib/RT/Shredder.pm b/rt/lib/RT/Shredder.pm
index 40c73b36d..4f96e162d 100644
--- a/rt/lib/RT/Shredder.pm
+++ b/rt/lib/RT/Shredder.pm
@@ -539,9 +539,9 @@ sub WipeoutAll
{
my $self = $_[0];
- while ( my ($k, $v) = each %{ $self->{'cache'} } ) {
- next if $v->{'State'} & (WIPED | IN_WIPING);
- $self->Wipeout( Object => $v->{'Object'} );
+ foreach my $cache_val ( values %{ $self->{'cache'} } ) {
+ next if $cache_val->{'State'} & (WIPED | IN_WIPING);
+ $self->Wipeout( Object => $cache_val->{'Object'} );
}
}
diff --git a/rt/lib/RT/Test.pm b/rt/lib/RT/Test.pm
index 7d69dd60d..3e7c910ec 100644
--- a/rt/lib/RT/Test.pm
+++ b/rt/lib/RT/Test.pm
@@ -131,14 +131,14 @@ sub import {
if (RT->Config->Get('DevelMode')) { require Module::Refresh; }
- $class->bootstrap_db( %args );
-
RT::InitPluginPaths();
+ RT::InitClasses();
+
+ $class->bootstrap_db( %args );
__reconnect_rt()
unless $args{nodb};
- RT::InitClasses();
RT::InitLogging();
RT->Plugins;
diff --git a/rt/lib/RT/Ticket.pm b/rt/lib/RT/Ticket.pm
index 00f88b657..577c44429 100755
--- a/rt/lib/RT/Ticket.pm
+++ b/rt/lib/RT/Ticket.pm
@@ -1124,7 +1124,7 @@ sub AddWatcher {
return (0, $self->loc("Couldn't parse address from '[_1]' string", $args{'Email'} ))
unless $addr;
- if ( lc $self->CurrentUser->UserObj->EmailAddress
+ if ( lc $self->CurrentUser->EmailAddress
eq lc RT::User->CanonicalizeEmailAddress( $addr->address ) )
{
$args{'PrincipalId'} = $self->CurrentUser->id;
@@ -1305,7 +1305,7 @@ sub DeleteWatcher {
}
}
else {
- $RT::Logger->warn("$self -> DeleteWatcher got passed a bogus type");
+ $RT::Logger->warning("$self -> DeleteWatcher got passed a bogus type");
return ( 0,
$self->loc('Error in parameters to Ticket->DeleteWatcher') );
}
@@ -1989,6 +1989,31 @@ sub FirstActiveStatus {
return $next;
}
+=head2 FirstInactiveStatus
+
+Returns the first inactive status that the ticket could transition to,
+according to its current Queue's lifecycle. May return undef if there
+is no such possible status to transition to, or we are already in it.
+This is used in resolve action in UnsafeEmailCommands, for instance.
+
+=cut
+
+sub FirstInactiveStatus {
+ my $self = shift;
+
+ my $lifecycle = $self->QueueObj->Lifecycle;
+ my $status = $self->Status;
+ my @inactive = $lifecycle->Inactive;
+ # no change if no inactive statuses in the lifecycle
+ return undef unless @inactive;
+
+ # no change if the ticket is already has first status from the list of inactive
+ return undef if lc $status eq lc $inactive[0];
+
+ my ($next) = grep $lifecycle->IsInactive($_), $lifecycle->Transitions($status);
+ return $next;
+}
+
=head2 SetStarted
Takes a date in ISO format or undef
@@ -2315,7 +2340,9 @@ sub _RecordNote {
my $msgid = $args{'MIMEObj'}->head->get('Message-ID');
unless (defined $msgid && $msgid =~ /<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$org\E>/) {
$args{'MIMEObj'}->head->set(
- 'RT-Message-ID' => RT::Interface::Email::GenMessageId( Ticket => $self )
+ 'RT-Message-ID' => Encode::encode_utf8(
+ RT::Interface::Email::GenMessageId( Ticket => $self )
+ )
);
}
@@ -3340,6 +3367,28 @@ sub SeenUpTo {
return $txns->First;
}
+=head2 RanTransactionBatch
+
+Acts as a guard around running TransactionBatch scrips.
+
+Should be false until you enter the code that runs TransactionBatch scrips
+
+Accepts an optional argument to indicate that TransactionBatch Scrips should no longer be run on this object.
+
+=cut
+
+sub RanTransactionBatch {
+ my $self = shift;
+ my $val = shift;
+
+ if ( defined $val ) {
+ return $self->{_RanTransactionBatch} = $val;
+ } else {
+ return $self->{_RanTransactionBatch};
+ }
+
+}
+
=head2 TransactionBatch
@@ -3376,6 +3425,22 @@ sub ApplyTransactionBatch {
sub _ApplyTransactionBatch {
my $self = shift;
+
+ return if $self->RanTransactionBatch;
+ $self->RanTransactionBatch(1);
+
+ my $still_exists = RT::Ticket->new( RT->SystemUser );
+ $still_exists->Load( $self->Id );
+ if (not $still_exists->Id) {
+ # The ticket has been removed from the database, but we still
+ # have pending TransactionBatch txns for it. Unfortunately,
+ # because it isn't in the DB anymore, attempting to run scrips
+ # on it may produce unpredictable results; simply drop the
+ # batched transactions.
+ $RT::Logger->warning("TransactionBatch was fired on a ticket that no longer exists; unable to run scrips! Call ->ApplyTransactionBatch before shredding the ticket, for consistent results.");
+ return;
+ }
+
my $batch = $self->TransactionBatch;
my %seen;
@@ -3423,10 +3488,7 @@ sub DESTROY {
return;
}
- my $batch = $self->TransactionBatch;
- return unless $batch && @$batch;
-
- return $self->_ApplyTransactionBatch;
+ return $self->ApplyTransactionBatch;
}
diff --git a/rt/lib/RT/Tickets.pm b/rt/lib/RT/Tickets.pm
index 485d7df53..c9986f41e 100755
--- a/rt/lib/RT/Tickets.pm
+++ b/rt/lib/RT/Tickets.pm
@@ -436,6 +436,10 @@ sub _LinkLimit {
my $is_null = 0;
$is_null = 1 if !$value || $value =~ /^null$/io;
+ unless ($is_null) {
+ $value = RT::URI->new( $sb->CurrentUser )->CanonicalizeURI( $value );
+ }
+
my $direction = $meta->[1] || '';
my ($matchfield, $linkfield) = ('', '');
if ( $direction eq 'To' ) {
@@ -1651,6 +1655,7 @@ sub _CustomFieldLimit {
FIELD => $column,
OPERATOR => $op,
VALUE => $value,
+ CASESENSITIVE => 0,
%rest
) );
$self->_CloseParen;
@@ -1713,6 +1718,7 @@ sub _CustomFieldLimit {
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
+ CASESENSITIVE => 0,
%rest
);
}
@@ -1739,6 +1745,7 @@ sub _CustomFieldLimit {
OPERATOR => $op,
VALUE => $value,
ENTRYAGGREGATOR => 'AND',
+ CASESENSITIVE => 0,
) );
}
}
@@ -1748,6 +1755,7 @@ sub _CustomFieldLimit {
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
+ CASESENSITIVE => 0,
%rest
);
@@ -1774,6 +1782,7 @@ sub _CustomFieldLimit {
OPERATOR => $op,
VALUE => $value,
ENTRYAGGREGATOR => 'AND',
+ CASESENSITIVE => 0,
) );
$self->_CloseParen;
}
@@ -1830,6 +1839,7 @@ sub _CustomFieldLimit {
FIELD => $column,
OPERATOR => $op,
VALUE => $value,
+ CASESENSITIVE => 0,
) );
}
else {
@@ -1839,6 +1849,7 @@ sub _CustomFieldLimit {
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
+ CASESENSITIVE => 0,
);
}
$self->_SQLLimit(
diff --git a/rt/lib/RT/Transaction.pm b/rt/lib/RT/Transaction.pm
index bd4d83546..3344687da 100755
--- a/rt/lib/RT/Transaction.pm
+++ b/rt/lib/RT/Transaction.pm
@@ -133,12 +133,6 @@ sub Create {
return ( 0, $self->loc( "Transaction->Create couldn't, as you didn't specify an object type and id"));
}
-
- # Set up any custom fields passed at creation. Has to happen
- # before scrips.
-
- $self->UpdateCustomFields(%{ $args{'CustomFields'} });
-
#lets create our transaction
my %params = (
Type => $args{'Type'},
@@ -169,6 +163,11 @@ sub Create {
}
}
+ # Set up any custom fields passed at creation. Has to happen
+ # before scrips.
+
+ $self->UpdateCustomFields(%{ $args{'CustomFields'} });
+
$self->AddAttribute(
Name => 'SquelchMailTo',
Content => RT::User->CanonicalizeEmailAddress($_)
diff --git a/rt/lib/RT/URI.pm b/rt/lib/RT/URI.pm
index fce04598a..284a75ee0 100644
--- a/rt/lib/RT/URI.pm
+++ b/rt/lib/RT/URI.pm
@@ -91,7 +91,26 @@ sub new {
return ($self);
}
+=head2 CanonicalizeURI <URI>
+Returns the canonical form of the given URI by calling L</FromURI> and then L</URI>.
+
+If the URI is unparseable by FromURI the passed in URI is simply returned untouched.
+
+=cut
+
+sub CanonicalizeURI {
+ my $self = shift;
+ my $uri = shift;
+ if ($self->FromURI($uri)) {
+ my $canonical = $self->URI;
+ if ($canonical and $uri ne $canonical) {
+ RT->Logger->debug("Canonicalizing URI '$uri' to '$canonical'");
+ $uri = $canonical;
+ }
+ }
+ return $uri;
+}
=head2 FromObject <Object>
diff --git a/rt/lib/RT/User.pm b/rt/lib/RT/User.pm
index 9b4a82683..e7f7c2ad6 100755
--- a/rt/lib/RT/User.pm
+++ b/rt/lib/RT/User.pm
@@ -932,7 +932,7 @@ sub IsPassword {
# crypt() output
return 0 unless crypt(encode_utf8($value), $stored) eq $stored;
} else {
- $RT::Logger->warn("Unknown password form");
+ $RT::Logger->warning("Unknown password form");
return 0;
}
diff --git a/rt/sbin/rt-server.fcgi.in b/rt/sbin/rt-server.fcgi.in
index 45c377088..f84f6c103 100644
--- a/rt/sbin/rt-server.fcgi.in
+++ b/rt/sbin/rt-server.fcgi.in
@@ -172,7 +172,7 @@ if (caller) {
require Plack::Runner;
my $is_fastcgi = $0 =~ m/fcgi$/;
-my $r = Plack::Runner->new( $0 =~ 'standalone' ? ( server => 'Standalone' ) :
+my $r = Plack::Runner->new( $0 =~ /standalone/ ? ( server => 'Standalone' ) :
$is_fastcgi ? ( server => 'FCGI' )
: (),
env => 'deployment' );
diff --git a/rt/sbin/rt-server.in b/rt/sbin/rt-server.in
index 45c377088..f84f6c103 100644
--- a/rt/sbin/rt-server.in
+++ b/rt/sbin/rt-server.in
@@ -172,7 +172,7 @@ if (caller) {
require Plack::Runner;
my $is_fastcgi = $0 =~ m/fcgi$/;
-my $r = Plack::Runner->new( $0 =~ 'standalone' ? ( server => 'Standalone' ) :
+my $r = Plack::Runner->new( $0 =~ /standalone/ ? ( server => 'Standalone' ) :
$is_fastcgi ? ( server => 'FCGI' )
: (),
env => 'deployment' );
diff --git a/rt/sbin/rt-test-dependencies.in b/rt/sbin/rt-test-dependencies.in
index 37ef32f64..960d640c3 100644
--- a/rt/sbin/rt-test-dependencies.in
+++ b/rt/sbin/rt-test-dependencies.in
@@ -56,9 +56,10 @@ no warnings qw(numeric redefine);
use Getopt::Long;
my %args;
my %deps;
+my @orig_argv = @ARGV;
GetOptions(
\%args, 'v|verbose',
- 'install', 'with-MYSQL',
+ 'install!', 'with-MYSQL',
'with-POSTGRESQL|with-pg|with-pgsql', 'with-SQLITE',
'with-ORACLE', 'with-FASTCGI',
'with-MODPERL1', 'with-MODPERL2',
@@ -293,7 +294,7 @@ Test::LongString
.
$deps{'FASTCGI'} = [ text_to_hash( << '.') ];
-FCGI
+FCGI 0.74
FCGI::ProcManager
.
@@ -344,7 +345,7 @@ URI 1.59
$deps{'GRAPHVIZ'} = [ text_to_hash( << '.') ];
GraphViz
-IPC::Run
+IPC::Run 0.90
.
$deps{'GD'} = [ text_to_hash( << '.') ];
@@ -359,6 +360,7 @@ Convert::Color
my %AVOID = (
'DBD::Oracle' => [qw(1.23)],
+ 'Email::Address' => [qw(1.893 1.894)],
);
if ($args{'download'}) {
@@ -403,7 +405,12 @@ foreach my $type (sort grep $args{$_}, keys %args) {
$Missing_By_Type{$type} = \%missing if keys %missing;
}
-conclude(%Missing_By_Type);
+if ( $args{'install'} && keys %Missing_By_Type ) {
+ exec($0, @orig_argv, '--no-install');
+}
+else {
+ conclude(%Missing_By_Type);
+}
sub test_deps {
my @deps = @_;
diff --git a/rt/sbin/standalone_httpd b/rt/sbin/standalone_httpd
index 3386cd1fe..cef0f3102 100755
--- a/rt/sbin/standalone_httpd
+++ b/rt/sbin/standalone_httpd
@@ -172,7 +172,7 @@ if (caller) {
require Plack::Runner;
my $is_fastcgi = $0 =~ m/fcgi$/;
-my $r = Plack::Runner->new( $0 =~ 'standalone' ? ( server => 'Standalone' ) :
+my $r = Plack::Runner->new( $0 =~ /standalone/ ? ( server => 'Standalone' ) :
$is_fastcgi ? ( server => 'FCGI' )
: (),
env => 'deployment' );
diff --git a/rt/sbin/standalone_httpd.in b/rt/sbin/standalone_httpd.in
index 45c377088..f84f6c103 100644
--- a/rt/sbin/standalone_httpd.in
+++ b/rt/sbin/standalone_httpd.in
@@ -172,7 +172,7 @@ if (caller) {
require Plack::Runner;
my $is_fastcgi = $0 =~ m/fcgi$/;
-my $r = Plack::Runner->new( $0 =~ 'standalone' ? ( server => 'Standalone' ) :
+my $r = Plack::Runner->new( $0 =~ /standalone/ ? ( server => 'Standalone' ) :
$is_fastcgi ? ( server => 'FCGI' )
: (),
env => 'deployment' );
diff --git a/rt/share/html/Admin/Queues/Modify.html b/rt/share/html/Admin/Queues/Modify.html
index 5682eee28..85cd62f16 100755
--- a/rt/share/html/Admin/Queues/Modify.html
+++ b/rt/share/html/Admin/Queues/Modify.html
@@ -51,7 +51,7 @@
-<form action="<%RT->Config->Get('WebPath')%>/Admin/Queues/Modify.html" name="ModifyQueue" method="post">
+<form action="<%RT->Config->Get('WebPath')%>/Admin/Queues/Modify.html" name="ModifyQueue" method="post" enctype="multipart/form-data">
<input type="hidden" class="hidden" name="SetEnabled" value="1" />
<input type="hidden" class="hidden" name="id" value="<% $Create? 'new': $QueueObj->Id %>" />
diff --git a/rt/share/html/Approvals/Elements/PendingMyApproval b/rt/share/html/Approvals/Elements/PendingMyApproval
index d2061da84..169c25cb6 100755
--- a/rt/share/html/Approvals/Elements/PendingMyApproval
+++ b/rt/share/html/Approvals/Elements/PendingMyApproval
@@ -74,7 +74,7 @@ $tickets->LimitOwner( VALUE => $session{'CurrentUser'}->Id );
# also consider AdminCcs as potential approvers.
my $group_tickets = RT::Tickets->new( $session{'CurrentUser'} );
-$group_tickets->LimitWatcher( VALUE => $session{'CurrentUser'}->UserObj->EmailAddress, TYPE => 'AdminCc' );
+$group_tickets->LimitWatcher( VALUE => $session{'CurrentUser'}->EmailAddress, TYPE => 'AdminCc' );
my $created_before = RT::Date->new( $session{'CurrentUser'} );
my $created_after = RT::Date->new( $session{'CurrentUser'} );
diff --git a/rt/share/html/Approvals/autohandler b/rt/share/html/Approvals/autohandler
index a05770654..3e0f2c6db 100644
--- a/rt/share/html/Approvals/autohandler
+++ b/rt/share/html/Approvals/autohandler
@@ -46,8 +46,13 @@
%#
%# END BPS TAGGED BLOCK }}}
<%init>
-$m->call_next(%ARGS) if $session{'CurrentUser'}->UserObj->HasRight(
+if ( $session{'CurrentUser'}->UserObj->HasRight(
Right => 'ShowApprovalsTab',
Object => $RT::System,
-);
+) ) {
+ $m->call_next(%ARGS);
+}
+else {
+ Abort("No permission to view approval");
+}
</%init>
diff --git a/rt/share/html/Dashboards/Subscription.html b/rt/share/html/Dashboards/Subscription.html
index 3669e4687..3a57102c7 100644
--- a/rt/share/html/Dashboards/Subscription.html
+++ b/rt/share/html/Dashboards/Subscription.html
@@ -171,7 +171,7 @@
<&|/l&>Recipient</&>:
</td><td class="value">
<input name="Recipient" id="Recipient" size="30" value="<%$fields{Recipient} ? $fields{Recipient} : ''%>" />
-<div class="hints"><% loc("Leave blank to send to your current email address ([_1])", $session{'CurrentUser'}->UserObj->EmailAddress) %></div>
+<div class="hints"><% loc("Leave blank to send to your current email address ([_1])", $session{'CurrentUser'}->EmailAddress) %></div>
</td></tr>
</table>
</&>
diff --git a/rt/share/html/Elements/AddCustomers b/rt/share/html/Elements/AddCustomers
index 9828d7d53..6517db42c 100644
--- a/rt/share/html/Elements/AddCustomers
+++ b/rt/share/html/Elements/AddCustomers
@@ -41,7 +41,7 @@ my @Customers = ();
if ( $CustomerString ) {
@Customers = &RT::URI::freeside::smart_search(
'search' => $CustomerString,
- 'no_fuzzy_on_exact' => 1, #pref?
+ 'no_fuzzy_on_exact' => ! $FS::CurrentUser::CurrentUser->option('enable_fuzzy_on_exact'),
);
}
diff --git a/rt/share/html/Elements/ColumnMap b/rt/share/html/Elements/ColumnMap
index b9c3b4bc8..f268a5d1c 100644
--- a/rt/share/html/Elements/ColumnMap
+++ b/rt/share/html/Elements/ColumnMap
@@ -118,7 +118,7 @@ my $COLUMN_MAP = {
CheckBox => {
title => sub {
my $name = $_[1] || 'SelectedTickets';
- my $checked = $m->request_args->{ $name .'All' }? 'checked="checked"': '';
+ my $checked = $DECODED_ARGS->{ $name .'All' }? 'checked="checked"': '';
return \qq{<input type="checkbox" name="}, $name, \qq{All" value="1" $checked
onclick="setCheckbox(this.form, },
@@ -130,9 +130,9 @@ my $COLUMN_MAP = {
my $name = $_[2] || 'SelectedTickets';
return \qq{<input type="checkbox" name="}, $name, \qq{" value="$id" checked="checked" />}
- if $m->request_args->{ $name . 'All'};
+ if $DECODED_ARGS->{ $name . 'All'};
- my $arg = $m->request_args->{ $name };
+ my $arg = $DECODED_ARGS->{ $name };
my $checked = '';
if ( $arg && ref $arg ) {
$checked = 'checked="checked"' if grep $_ == $id, @$arg;
@@ -149,7 +149,7 @@ my $COLUMN_MAP = {
my $id = $_[0]->id;
my $name = $_[2] || 'SelectedTicket';
- my $arg = $m->request_args->{ $name };
+ my $arg = $DECODED_ARGS->{ $name };
my $checked = '';
$checked = 'checked="checked"' if $arg && $arg == $id;
return \qq{<input type="radio" name="}, $name, \qq{" value="$id" $checked />};
diff --git a/rt/share/html/Elements/EditCustomField b/rt/share/html/Elements/EditCustomField
index b74c4844e..8b87fd425 100644
--- a/rt/share/html/Elements/EditCustomField
+++ b/rt/share/html/Elements/EditCustomField
@@ -71,7 +71,7 @@ if ( $Object && $Object->id ) {
# Always fill $Default with submited values if it's empty
if ( ( !defined $Default || !length $Default ) && $DefaultsFromTopArguments ) {
- my %TOP = $m->request_args;
+ my %TOP = %$DECODED_ARGS;
$Default = $TOP{ $NamePrefix .$CustomField->Id . '-Values' }
|| $TOP{ $NamePrefix .$CustomField->Id . '-Value' };
}
diff --git a/rt/share/html/Elements/Header b/rt/share/html/Elements/Header
index 1830c4bf2..65d06f879 100755
--- a/rt/share/html/Elements/Header
+++ b/rt/share/html/Elements/Header
@@ -130,7 +130,8 @@ if ($m->comp_exists($stylesheet_plugin) ) {
# $m->callback( %ARGS, CallbackName => 'Head' );
$head .= $m->scomp( '/Elements/Callback', _CallbackName => 'Head', %ARGS );
-my $etc = qq[ class="\L$style" ];
+my $sbs = RT->Config->Get("UseSideBySideLayout", $session{'CurrentUser'}) ? ' sidebyside' : '';
+my $etc = qq[ class="\L$style$sbs" ];
$etc .= qq[ id="comp-$id"] if $id;
</%INIT>
diff --git a/rt/share/html/Elements/HeaderJavascript b/rt/share/html/Elements/HeaderJavascript
index 28788db57..d5741f4e6 100644
--- a/rt/share/html/Elements/HeaderJavascript
+++ b/rt/share/html/Elements/HeaderJavascript
@@ -67,7 +67,7 @@ $onload => undef
% }
% if ( $RichText and RT->Config->Get('MessageBoxRichText', $session{'CurrentUser'})) {
- jQuery().ready(function () { ReplaceAllTextareas(<%$m->request_args->{'CKeditorEncoded'} || 0 |n,j%>) });
+ jQuery().ready(function () { ReplaceAllTextareas(<%$DECODED_ARGS->{'CKeditorEncoded'} || 0 |n,j%>) });
% }
--></script>
<%ARGS>
diff --git a/rt/share/html/Elements/ListActions b/rt/share/html/Elements/ListActions
index 999d3fe5b..8929ff731 100755
--- a/rt/share/html/Elements/ListActions
+++ b/rt/share/html/Elements/ListActions
@@ -65,7 +65,7 @@ if ( ref( $session{'Actions'}{''} ) eq 'ARRAY' ) {
unshift @actions, @{ delete $session{'Actions'}{''} };
}
-my $actions_pointer = $m->request_args->{'results'};
+my $actions_pointer = $DECODED_ARGS->{'results'};
if ($actions_pointer && ref( $session{'Actions'}->{$actions_pointer} ) eq 'ARRAY' ) {
unshift @actions, @{ delete $session{'Actions'}->{$actions_pointer} };
diff --git a/rt/share/html/Elements/MessageBox b/rt/share/html/Elements/MessageBox
index 61995e057..69227bfa9 100755
--- a/rt/share/html/Elements/MessageBox
+++ b/rt/share/html/Elements/MessageBox
@@ -46,7 +46,7 @@
%#
%# END BPS TAGGED BLOCK }}}
<textarea autocomplete="off" class="messagebox" <% $width_attr %>="<% $Width %>" rows="<% $Height %>" <% $wrap_type |n %> name="<% $Name %>" id="<% $Name %>">\
-% $m->comp('/Articles/Elements/IncludeArticle', %ARGS);
+% $m->comp('/Articles/Elements/IncludeArticle', %ARGS) if $IncludeArticle;
% $m->callback( %ARGS, SignatureRef => \$signature );
<% $Default || '' %><% $message %><% $signature %></textarea>
% $m->callback( %ARGS, CallbackName => 'AfterTextArea' );
@@ -89,4 +89,5 @@ $Width => RT->Config->Get('MessageBoxWidth', $session{'CurrentUser'}
$Height => RT->Config->Get('MessageBoxHeight', $session{'CurrentUser'} ) || 15
$Wrap => RT->Config->Get('MessageBoxWrap', $session{'CurrentUser'} ) || 'SOFT'
$IncludeSignature => RT->Config->Get('MessageBoxIncludeSignature');
+$IncludeArticle => 1;
</%ARGS>
diff --git a/rt/share/html/Elements/QueueSummaryByStatus b/rt/share/html/Elements/QueueSummaryByStatus
index 09f274f74..f649d2850 100644
--- a/rt/share/html/Elements/QueueSummaryByStatus
+++ b/rt/share/html/Elements/QueueSummaryByStatus
@@ -122,9 +122,13 @@ my $statuses = {};
use RT::Report::Tickets;
my $report = RT::Report::Tickets->new( RT->SystemUser );
-my $query = @queues
- ? join(' OR ', map "Queue = ".$_->{id}, @queues)
- : 'id < 0';
+my $query =
+ "(".
+ join(" OR ", map {s{(['\\])}{\\$1}g; "Status = '$_'"} @statuses) #'
+ .") AND (".
+ join(' OR ', map "Queue = ".$_->{id}, @queues)
+ .")";
+$query = 'id < 0' unless @queues;
$report->SetupGroupings( Query => $query, GroupBy => [qw(Status Queue)] );
while ( my $entry = $report->Next ) {
diff --git a/rt/share/html/Elements/RT__CustomField/ColumnMap b/rt/share/html/Elements/RT__CustomField/ColumnMap
index ecb219d9e..b04398434 100644
--- a/rt/share/html/Elements/RT__CustomField/ColumnMap
+++ b/rt/share/html/Elements/RT__CustomField/ColumnMap
@@ -118,7 +118,7 @@ my $COLUMN_MAP = {
RemoveCheckBox => {
title => sub {
my $name = 'RemoveCustomField';
- my $checked = $m->request_args->{ $name .'All' }? 'checked="checked"': '';
+ my $checked = $DECODED_ARGS->{ $name .'All' }? 'checked="checked"': '';
return \qq{<input type="checkbox" name="}, $name, \qq{All" value="1" $checked
onclick="setCheckbox(this.form, },
@@ -130,7 +130,7 @@ my $COLUMN_MAP = {
return '' if $_[0]->IsApplied;
my $name = 'RemoveCustomField';
- my $arg = $m->request_args->{ $name };
+ my $arg = $DECODED_ARGS->{ $name };
my $checked = '';
if ( $arg && ref $arg ) {
diff --git a/rt/share/html/Elements/SelectWatcherType b/rt/share/html/Elements/SelectWatcherType
index 44beee00d..4f1df60b2 100755
--- a/rt/share/html/Elements/SelectWatcherType
+++ b/rt/share/html/Elements/SelectWatcherType
@@ -56,7 +56,7 @@
<%INIT>
my @types;
-if ($Scope =~ 'queue') {
+if ($Scope =~ /queue/) {
@types = RT::Queue->ManageableRoleGroupTypes;
}
else {
diff --git a/rt/share/html/Elements/Tabs b/rt/share/html/Elements/Tabs
index 3193b488d..3aac9d803 100755
--- a/rt/share/html/Elements/Tabs
+++ b/rt/share/html/Elements/Tabs
@@ -845,7 +845,7 @@ my $build_selfservice_nav = sub {
} elsif ( $queue_id ) {
Menu->child( new => title => loc('New ticket'), path => '/SelfService/Create.html?Queue=' . $queue_id );
}
- my $tickets = Menu->child( tickets => title => loc('Tickets'));
+ my $tickets = Menu->child( tickets => title => loc('Tickets'), path => '/SelfService/' );
$tickets->child( open => title => loc('Open tickets'), path => '/SelfService/' );
$tickets->child( closed => title => loc('Closed tickets'), path => '/SelfService/Closed.html' );
diff --git a/rt/share/html/Helpers/Autocomplete/Users b/rt/share/html/Helpers/Autocomplete/Users
index dbc2d888f..c2b92c1bf 100644
--- a/rt/share/html/Helpers/Autocomplete/Users
+++ b/rt/share/html/Helpers/Autocomplete/Users
@@ -116,6 +116,9 @@ foreach (split /\s*,\s*/, $exclude) {
my @suggestions;
+$users->Limit( FIELD => $return, OPERATOR => '!=', VALUE => '' );
+$users->Limit( FIELD => $return, OPERATOR => 'IS NOT', VALUE => 'NULL', ENTRYAGGREGATOR => 'AND' );
+
while ( my $user = $users->Next ) {
next if $user->id == RT->SystemUser->id
or $user->id == RT->Nobody->id;
diff --git a/rt/share/html/NoAuth/css/aileron/boxes.css b/rt/share/html/NoAuth/css/aileron/boxes.css
index ed6623cba..f90ac9f77 100644
--- a/rt/share/html/NoAuth/css/aileron/boxes.css
+++ b/rt/share/html/NoAuth/css/aileron/boxes.css
@@ -91,10 +91,6 @@
.titlebox .titlebox-title {
position: relative;
- /* This is for [rt3 #19044]. Move it to an IE-specific file if it causes
- * problems. If we remove CSS3PIE, it can also probably go away, although it
- * probably won't hurt. */
- z-index: 1;
}
.titlebox .titlebox-title a {
diff --git a/rt/share/html/NoAuth/css/aileron/ticket.css b/rt/share/html/NoAuth/css/aileron/ticket.css
index 4d069d9f9..7b573f72c 100644
--- a/rt/share/html/NoAuth/css/aileron/ticket.css
+++ b/rt/share/html/NoAuth/css/aileron/ticket.css
@@ -87,8 +87,7 @@ div#ticket-history {
float: left;
margin: 0.25em 0.70em 0.25em 0.25em;
width: 1em;
- height: 1.25em;
- padding: 0.75em 0 0 0;
+ padding: 0;
border-right: 1px solid #999;
border-bottom: 1px solid #999;
-moz-border-radius-bottomright: 0.25em;
@@ -100,6 +99,16 @@ div#ticket-history {
div#ticket-history span.type a {
color: #fff;
+ padding-top: 0.75em;
+ display: block;
+}
+
+#ticket-history a#lasttrans {
+ display: inline;
+ height: 0;
+ width: 0;
+ padding: 0;
+ margin: 0;
}
diff --git a/rt/share/html/NoAuth/css/ballard/boxes.css b/rt/share/html/NoAuth/css/ballard/boxes.css
index 912ac55f4..9610cd5e7 100644
--- a/rt/share/html/NoAuth/css/ballard/boxes.css
+++ b/rt/share/html/NoAuth/css/ballard/boxes.css
@@ -54,6 +54,7 @@
margin-left: 1em;
-moz-border-radius: 0.5em;
-webkit-border-radius: 0.5em;
+ border-radius: 0.5em;
margin-bottom: 2em;
border-bottom: 2px solid #aaa;
border-right: 2px solid #aaa;
@@ -71,6 +72,7 @@
margin-top: 1em;
-moz-border-radius: 0.5em;
-webkit-border-radius: 0.5em;
+ border-radius: 0.5em;
margin-right: 0.25em;
}
@@ -114,6 +116,7 @@
padding-right: 0.75em;
-moz-border-radius: 0.5em;
-webkit-border-radius: 0.5em;
+ border-radius: 0.5em;
border-bottom: 2px solid #aaa;
border-right: 2px solid #aaa;
@@ -138,10 +141,12 @@
padding-top: 0.5em;
-moz-border-radius-bottomleft: 0.25em;
-webkit-border-bottom-left-radius: 0.25em;
+ border-bottom-left-radius: 0.25em;
-moz-border-radius-topright: 0.25em;
-webkit-border-top-right-radius: 0.25em;
+ border-top-right-radius: 0.25em;
}
diff --git a/rt/share/html/NoAuth/css/ballard/layout.css b/rt/share/html/NoAuth/css/ballard/layout.css
index 8dc0cc162..8b600b828 100644
--- a/rt/share/html/NoAuth/css/ballard/layout.css
+++ b/rt/share/html/NoAuth/css/ballard/layout.css
@@ -60,8 +60,10 @@ div#body {
padding: 1.8em 1em 1em 1em;
-moz-border-radius-topleft: 0.5em;
-webkit-border-top-left-radius: 0.5em;
+ border-top-left-radius: 0.5em;
-moz-border-radius-bottomleft: 0.5em;
-webkit-border-bottom-left-radius: 0.5em;
+ border-bottom-left-radius: 0.5em;
margin-left: 10em;
margin-top: 3em;
margin-right: 0;
@@ -89,8 +91,10 @@ div#footer {
border-left: 2px solid #aaa;
-moz-border-radius-topleft: 0.5em;
-webkit-border-top-left-radius: 0.5em;
+ border-top-left-radius: 0.5em;
-moz-border-radius-bottomleft: 0.5em;
-webkit-border-bottom-left-radius: 0.5em;
+ border-bottom-left-radius: 0.5em;
}
div#footer #time {
diff --git a/rt/share/html/NoAuth/css/ballard/nav.css b/rt/share/html/NoAuth/css/ballard/nav.css
index 196f0e6c0..dc29818fe 100644
--- a/rt/share/html/NoAuth/css/ballard/nav.css
+++ b/rt/share/html/NoAuth/css/ballard/nav.css
@@ -49,8 +49,10 @@
background-color: #fff;
-moz-border-radius-bottomright: 0.5em;
-webkit-border-bottom-right-radius: 0.5em;
+ border-bottom-right-radius: 0.5em;
-moz-border-radius-topright: 0.5em;
-webkit-border-top-right-radius: 0.5em;
+ border-top-right-radius: 0.5em;
width: 10em;
font-size: 0.85em;
position: absolute;
@@ -130,6 +132,7 @@
border: 1px solid #ccc;
-moz-border-radius-bottomleft: 0.5em;
-webkit-border-bottom-left-radius: 0.5em;
+ border-bottom-left-radius: 0.5em;
padding: 0;
padding-top: 0.5em;
padding-right: 0.5em;
diff --git a/rt/share/html/NoAuth/css/ballard/ticket-search.css b/rt/share/html/NoAuth/css/ballard/ticket-search.css
index 19ee847ff..fb252b5e3 100644
--- a/rt/share/html/NoAuth/css/ballard/ticket-search.css
+++ b/rt/share/html/NoAuth/css/ballard/ticket-search.css
@@ -163,6 +163,7 @@
border-bottom: 1px solid #999;
-moz-border-radius-bottomleft: 0.5em;
-webkit-border-bottom-left-radius: 0.5em;
+ border-bottom-left-radius: 0.5em;
}
diff --git a/rt/share/html/NoAuth/css/ballard/ticket.css b/rt/share/html/NoAuth/css/ballard/ticket.css
index 06b6678c9..4d416e175 100644
--- a/rt/share/html/NoAuth/css/ballard/ticket.css
+++ b/rt/share/html/NoAuth/css/ballard/ticket.css
@@ -77,6 +77,7 @@ div#ticket-history {
color: #ccc;
-moz-border-radius-bottomleft: 0.5em;
-webkit-border-bottom-left-radius: 0.5em;
+ border-bottom-left-radius: 0.5em;
white-space: nowrap;
}
@@ -91,6 +92,7 @@ div#ticket-history {
border-bottom: 1px solid #999;
-moz-border-radius: 0.25em;
-webkit-border-bottom-right-radius: 0.25em;
+ border-bottom-right-radius: 0.25em;
}
div#ticket-history span.type a {
@@ -150,6 +152,7 @@ border-bottom: 2px solid #aaa;
margin-top: 0.5em;
-moz-border-radius: 0.5em;
-webkit-border-radius: 0.5em;
+border-radius: 0.5em;
}
diff --git a/rt/share/html/NoAuth/css/base/forms.css b/rt/share/html/NoAuth/css/base/forms.css
index eab97b19b..19af1b2a3 100644
--- a/rt/share/html/NoAuth/css/base/forms.css
+++ b/rt/share/html/NoAuth/css/base/forms.css
@@ -87,6 +87,7 @@ input[type=reset], input[type=submit], input[class=button], button {
padding-right: 0.5em;
-moz-border-radius: 0.5em;
-webkit-border-radius: 0.5em;
+ border-radius: 0.5em;
}
input.button:hover, button:hover, input[type=reset]:hover, input[type=submit]:hover, input[class=button]:hover {
diff --git a/rt/share/html/NoAuth/css/base/jquery-ui-timepicker-addon.css b/rt/share/html/NoAuth/css/base/jquery-ui-timepicker-addon.css
new file mode 100644
index 000000000..7eb871568
--- /dev/null
+++ b/rt/share/html/NoAuth/css/base/jquery-ui-timepicker-addon.css
@@ -0,0 +1,7 @@
+.ui-timepicker-div .ui-widget-header { margin-bottom: 8px; }
+.ui-timepicker-div dl { text-align: left; }
+.ui-timepicker-div dl dt { height: 25px; margin-bottom: -25px; }
+.ui-timepicker-div dl dd { margin: 0 10px 10px 65px; }
+.ui-timepicker-div td { font-size: 90%; }
+.ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; }
+.ui-datepicker-buttonpane button.ui-datepicker-current { opacity: 1.0; }
diff --git a/rt/share/html/NoAuth/css/base/jquery-ui.css b/rt/share/html/NoAuth/css/base/jquery-ui.css
index 820996ea8..8fe4f1545 100644
--- a/rt/share/html/NoAuth/css/base/jquery-ui.css
+++ b/rt/share/html/NoAuth/css/base/jquery-ui.css
@@ -46,5 +46,3 @@
%#
%# END BPS TAGGED BLOCK }}}
@import "jquery-ui.custom.modified.css";
-@import "ui.timepickr.css";
-@import "ui.timepickr.custom.css";
diff --git a/rt/share/html/NoAuth/css/base/jquery-ui.custom.modified.css b/rt/share/html/NoAuth/css/base/jquery-ui.custom.modified.css
index 7a323229a..3b1e1a00e 100644
--- a/rt/share/html/NoAuth/css/base/jquery-ui.custom.modified.css
+++ b/rt/share/html/NoAuth/css/base/jquery-ui.custom.modified.css
@@ -452,3 +452,27 @@
width: 200px; /*must have*/
height: 200px; /*must have*/
}
+/*
+ * jQuery UI Slider 1.8.4
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider#theming
+ */
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }
diff --git a/rt/share/html/NoAuth/css/base/main.css b/rt/share/html/NoAuth/css/base/main.css
index 9f77c8aee..dac733d87 100644
--- a/rt/share/html/NoAuth/css/base/main.css
+++ b/rt/share/html/NoAuth/css/base/main.css
@@ -49,6 +49,7 @@
@import "yui-fonts.css";
@import "jquery-ui.css";
+@import "jquery-ui-timepicker-addon.css";
@import "superfish.css";
@import "superfish-navbar.css";
@import "superfish-vertical.css";
diff --git a/rt/share/html/NoAuth/css/base/superfish-navbar.css b/rt/share/html/NoAuth/css/base/superfish-navbar.css
index 9a3f24cd9..459156ec7 100644
--- a/rt/share/html/NoAuth/css/base/superfish-navbar.css
+++ b/rt/share/html/NoAuth/css/base/superfish-navbar.css
@@ -90,4 +90,6 @@ ul.sf-navbar .current ul ul {
-moz-border-radius-topright: 0;
-webkit-border-top-right-radius: 0;
-webkit-border-bottom-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: 0;
}
diff --git a/rt/share/html/NoAuth/css/base/superfish.css b/rt/share/html/NoAuth/css/base/superfish.css
index 31198e423..7cb3b567c 100644
--- a/rt/share/html/NoAuth/css/base/superfish.css
+++ b/rt/share/html/NoAuth/css/base/superfish.css
@@ -130,6 +130,8 @@ li.sfHover > a > .sf-sub-indicator {
-moz-border-radius-topright: 17px;
-webkit-border-top-right-radius: 17px;
-webkit-border-bottom-left-radius: 17px;
+ border-top-right-radius: 17px;
+ border-bottom-left-radius: 17px;
}
.sf-shadow ul.sf-shadow-off {
background: transparent;
diff --git a/rt/share/html/NoAuth/css/base/ticket-form.css b/rt/share/html/NoAuth/css/base/ticket-form.css
index daab263b1..869eba774 100644
--- a/rt/share/html/NoAuth/css/base/ticket-form.css
+++ b/rt/share/html/NoAuth/css/base/ticket-form.css
@@ -82,21 +82,17 @@ iframe.richtext-editor {
.messagebox-container.action-response iframe
{
background-color: #fcc !important;
-}
-
-/*
-% if ( RT->Config->Get("UseSideBySideLayout", $session{'CurrentUser'}) ) {
-*/
+}
-#ticket-create-metadata,
-#ticket-update-metadata {
+.sidebyside #ticket-create-metadata,
+.sidebyside #ticket-update-metadata {
float: right;
width: 40%;
clear: right;
}
-#ticket-create-message,
-#ticket-update-message {
+.sidebyside #ticket-create-message,
+.sidebyside #ticket-update-message {
float: left;
width: 58%;
clear: left;
@@ -104,10 +100,10 @@ iframe.richtext-editor {
@media (max-width: 950px) {
/* Revert to a single column when we're less than 1000px wide */
- #ticket-create-metadata,
- #ticket-update-metadata,
- #ticket-create-message,
- #ticket-update-message
+ .sidebyside #ticket-create-metadata,
+ .sidebyside #ticket-update-metadata,
+ .sidebyside #ticket-create-message,
+ .sidebyside #ticket-update-message
{
float: none;
width: auto;
@@ -115,15 +111,12 @@ iframe.richtext-editor {
}
}
-#comp-Ticket-Update #body {
+.sidebyside #comp-Ticket-Update #body {
padding-top: 3em;
}
-#ticket-create-message .button[name="AddMoreAttach"],
-#ticket-update-message .button[name="AddMoreAttach"] {
+.sidebyside #ticket-create-message .button[name="AddMoreAttach"],
+.sidebyside #ticket-update-message .button[name="AddMoreAttach"] {
float: right;
}
-/*
-% }
-*/
diff --git a/rt/share/html/NoAuth/css/base/ui.timepickr.css b/rt/share/html/NoAuth/css/base/ui.timepickr.css
deleted file mode 100644
index e2dacf7a9..000000000
--- a/rt/share/html/NoAuth/css/base/ui.timepickr.css
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- jQuery ui.timepickr
- http://code.google.com/p/jquery-utils/
-
- copyright Maxime Haineault <haineault@gmail.com>
- http://haineault.com
-
- MIT License (http://www.opensource.org/licenses/mit-license.php
-*/
-.ui-timepickr {
- position:absolute;
- width:480px;
-}
-
-.ui-timepickr-row {
- margin:0;
- padding:0;
- margin-top:2px;
- display:none;
- position:relative;
-}
-
-.ui-timepickr-button {
- float:left;
- margin:0;
- padding:0;
- list-style:none;
- list-style-type:none;
-}
-
-.ui-timepickr-button span {
- font-size:.7em;
- padding:4px 6px 4px 6px;
- margin-left:2px;
- text-align:center;
- cursor:pointer;
- display:block;
- text-align:center;
-
-
- /* system theme (default) */
- border-width:1px;
- border-style:solid;
- /*border-color:ThreeDLightShadow ThreeDShadow ThreeDShadow ThreeDLightShadow;
- color:ButtonText;
- background:ButtonFace;*/
-}
-
-.ui-timepickr-button span.ui-state-hover {
- /*color:HighlightText;
- background:Highlight;*/
-}
-
-.ui-state-hover span {
- /*background:#c30;*/
-}
diff --git a/rt/share/html/NoAuth/css/base/ui.timepickr.custom.css b/rt/share/html/NoAuth/css/base/ui.timepickr.custom.css
deleted file mode 100644
index ad2aa66ce..000000000
--- a/rt/share/html/NoAuth/css/base/ui.timepickr.custom.css
+++ /dev/null
@@ -1,54 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%# <sales@bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-.ui-timepickr {
- font-size: 1.1em;
-}
-
-.ui-timepickr-button span {
- background: white;
-}
diff --git a/rt/share/html/NoAuth/css/web2/nav.css b/rt/share/html/NoAuth/css/web2/nav.css
index be63c5984..e404b61c8 100644
--- a/rt/share/html/NoAuth/css/web2/nav.css
+++ b/rt/share/html/NoAuth/css/web2/nav.css
@@ -239,6 +239,7 @@
border: 1px solid #ccc;
-moz-border-radius-bottomleft: 0.5em;
-webkit-border-bottom-left-radius: 0.5em;
+ border-bottom-left-radius: 0.5em;
border-right: none;
border-top: none;
list-style-type: none;
diff --git a/rt/share/html/NoAuth/js/jquery-ui-1.8.4.custom.min.js b/rt/share/html/NoAuth/js/jquery-ui-1.8.4.custom.min.js
index e90b4fe4b..0466005dc 100644
--- a/rt/share/html/NoAuth/js/jquery-ui-1.8.4.custom.min.js
+++ b/rt/share/html/NoAuth/js/jquery-ui-1.8.4.custom.min.js
@@ -222,3 +222,53 @@ c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(t
function(a){if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));
return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new L;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.4";window["DP_jQuery_"+y]=d})(jQuery);
;
+/*!
+ * jQuery UI Mouse 1.8.4
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */
+(function(c){c.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(b){return a._mouseDown(b)}).bind("click."+this.widgetName,function(b){if(a._preventClickEvent){a._preventClickEvent=false;b.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(a){a.originalEvent=a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&&
+this._mouseUp(a);this._mouseDownEvent=a;var b=this,e=a.which==1,f=typeof this.options.cancel=="string"?c(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!e||f||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){b.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();
+return true}}this._mouseMoveDelegate=function(d){return b._mouseMove(d)};this._mouseUpDelegate=function(d){return b._mouseUp(d)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);c.browser.safari||a.preventDefault();return a.originalEvent.mouseHandled=true}},_mouseMove:function(a){if(c.browser.msie&&!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&
+this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=a.target==this._mouseDownEvent.target;this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-
+a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
+/*
+ * jQuery UI Slider 1.8.4
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.slider",d.ui.mouse,{widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null},_create:function(){var a=this,b=this.options;this._mouseSliding=this._keySliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget ui-widget-content ui-corner-all");b.disabled&&this.element.addClass("ui-slider-disabled ui-disabled");
+this.range=d([]);if(b.range){if(b.range===true){this.range=d("<div></div>");if(!b.values)b.values=[this._valueMin(),this._valueMin()];if(b.values.length&&b.values.length!==2)b.values=[b.values[0],b.values[0]]}else this.range=d("<div></div>");this.range.appendTo(this.element).addClass("ui-slider-range");if(b.range==="min"||b.range==="max")this.range.addClass("ui-slider-range-"+b.range);this.range.addClass("ui-widget-header")}d(".ui-slider-handle",this.element).length===0&&d("<a href='#'></a>").appendTo(this.element).addClass("ui-slider-handle");
+if(b.values&&b.values.length)for(;d(".ui-slider-handle",this.element).length<b.values.length;)d("<a href='#'></a>").appendTo(this.element).addClass("ui-slider-handle");this.handles=d(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(c){c.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur();
+else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(c){d(this).data("index.ui-slider-handle",c)});this.handles.keydown(function(c){var e=true,f=d(this).data("index.ui-slider-handle"),h,g,i;if(!a.options.disabled){switch(c.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:e=
+false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");h=a._start(c,f);if(h===false)return}break}i=a.options.step;h=a.options.values&&a.options.values.length?(g=a.values(f)):(g=a.value());switch(c.keyCode){case d.ui.keyCode.HOME:g=a._valueMin();break;case d.ui.keyCode.END:g=a._valueMax();break;case d.ui.keyCode.PAGE_UP:g=a._trimAlignValue(h+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:g=a._trimAlignValue(h-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(h===
+a._valueMax())return;g=a._trimAlignValue(h+i);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(h===a._valueMin())return;g=a._trimAlignValue(h-i);break}a._slide(c,f,g);return e}}).keyup(function(c){var e=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(c,e);a._change(c,e);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");
+this._mouseDestroy();return this},_mouseCapture:function(a){var b=this.options,c,e,f,h,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});e=this._valueMax()-this._valueMin()+1;h=this;this.handles.each(function(i){var j=Math.abs(c-h.values(i));if(e>j){e=j;f=d(this);g=i}});if(b.range===true&&this.values(1)===b.min){g+=1;f=d(this.handles[g])}if(this._start(a,
+g)===false)return false;this._mouseSliding=true;h._handleIndex=g;f.addClass("ui-state-active").focus();b=f.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-f.width()/2,top:a.pageY-b.top-f.height()/2-(parseInt(f.css("borderTopWidth"),10)||0)-(parseInt(f.css("borderBottomWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0)};this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b=
+this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b=
+this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);
+c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var e;if(this.options.values&&this.options.values.length){e=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>e||b===1&&c<e))c=e;if(c!==this.values(b)){e=this.values();e[b]=c;a=this._trigger("slide",a,{handle:this.handles[b],value:c,values:e});this.values(b?0:1);a!==false&&this.values(b,c,true)}}else if(c!==this.value()){a=this._trigger("slide",a,{handle:this.handles[b],value:c});
+a!==false&&this.value(c)}},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("change",a,c)}},value:function(a){if(arguments.length){this.options.value=
+this._trimAlignValue(a);this._refreshValue();this._change(null,0)}return this._value()},values:function(a,b){var c,e,f;if(arguments.length>1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;e=arguments[0];for(f=0;f<c.length;f+=1){c[f]=this._trimAlignValue(e[f]);this._change(null,f)}this._refreshValue()}else return this.options.values&&this.options.values.length?this._values(a):this.value();
+else return this._values()},_setOption:function(a,b){var c,e=0;if(d.isArray(this.options.values))e=this.options.values.length;d.Widget.prototype._setOption.apply(this,arguments);switch(a){case "disabled":if(b){this.handles.filter(".ui-state-focus").blur();this.handles.removeClass("ui-state-hover");this.handles.attr("disabled","disabled");this.element.addClass("ui-disabled")}else{this.handles.removeAttr("disabled");this.element.removeClass("ui-disabled")}break;case "orientation":this._detectOrientation();
+this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue();break;case "value":this._animateOff=true;this._refreshValue();this._change(null,0);this._animateOff=false;break;case "values":this._animateOff=true;this._refreshValue();for(c=0;c<e;c+=1)this._change(null,c);this._animateOff=false;break}},_value:function(){var a=this.options.value;return a=this._trimAlignValue(a)},_values:function(a){var b,c;if(arguments.length){b=this.options.values[a];
+return b=this._trimAlignValue(b)}else{b=this.options.values.slice();for(c=0;c<b.length;c+=1)b[c]=this._trimAlignValue(b[c]);return b}},_trimAlignValue:function(a){if(a<this._valueMin())return this._valueMin();if(a>this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=a%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a=
+this.options.range,b=this.options,c=this,e=!this._animateOff?b.animate:false,f,h={},g,i,j,l;if(this.options.values&&this.options.values.length)this.handles.each(function(k){f=(c.values(k)-c._valueMin())/(c._valueMax()-c._valueMin())*100;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";d(this).stop(1,1)[e?"animate":"css"](h,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(k===0)c.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},b.animate);if(k===1)c.range[e?"animate":"css"]({width:f-
+g+"%"},{queue:false,duration:b.animate})}else{if(k===0)c.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},b.animate);if(k===1)c.range[e?"animate":"css"]({height:f-g+"%"},{queue:false,duration:b.animate})}g=f});else{i=this.value();j=this._valueMin();l=this._valueMax();f=l!==j?(i-j)/(l-j)*100:0;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";this.handle.stop(1,1)[e?"animate":"css"](h,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"},
+b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[e?"animate":"css"]({width:100-f+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[e?"animate":"css"]({height:100-f+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.4"})})(jQuery);
diff --git a/rt/share/html/NoAuth/js/jquery-ui-patch-datepicker.js b/rt/share/html/NoAuth/js/jquery-ui-patch-datepicker.js
index 40cc0db99..2ac101f93 100644
--- a/rt/share/html/NoAuth/js/jquery-ui-patch-datepicker.js
+++ b/rt/share/html/NoAuth/js/jquery-ui-patch-datepicker.js
@@ -58,4 +58,35 @@
return data;
};
+
+ $.datepicker._checkOffset_orig = $.datepicker._checkOffset;
+ $.datepicker._checkOffset = function(inst, offset, isFixed) {
+ // copied from the original
+ var dpHeight = inst.dpDiv.outerHeight();
+ var inputHeight = inst.input ? inst.input.outerHeight() : 0;
+ var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
+
+ // save the original offset rather than the new offset because the
+ // original function modifies the passed arg as a side-effect
+ var old_offset = { top: offset.top, left: offset.left };
+ offset = $.datepicker._checkOffset_orig(inst, offset, isFixed);
+
+ // Negate any up or down positioning by adding instead of subtracting
+ offset.top += Math.min(old_offset.top, (old_offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
+ Math.abs(dpHeight + inputHeight) : 0);
+
+ return offset;
+ };
+
+
+ $.timepicker._newInst_orig = $.timepicker._newInst;
+ $.timepicker._newInst = function($input, o) {
+ var tp_inst = $.timepicker._newInst_orig($input, o);
+ tp_inst._defaults.onClose = function(dateText, dp_inst) {
+ if ($.isFunction(o.onClose))
+ o.onClose.call($input[0], dateText, dp_inst, tp_inst);
+ };
+ return tp_inst;
+ };
+
})(jQuery);
diff --git a/rt/share/html/NoAuth/js/jquery-ui-timepicker-addon.js b/rt/share/html/NoAuth/js/jquery-ui-timepicker-addon.js
new file mode 100644
index 000000000..0a4ff026e
--- /dev/null
+++ b/rt/share/html/NoAuth/js/jquery-ui-timepicker-addon.js
@@ -0,0 +1,1326 @@
+/*
+* jQuery timepicker addon
+* By: Trent Richardson [http://trentrichardson.com]
+* Version 1.0.0
+* Last Modified: 02/05/2012
+*
+* Copyright 2012 Trent Richardson
+* Dual licensed under the MIT and GPL licenses.
+* http://trentrichardson.com/Impromptu/GPL-LICENSE.txt
+* http://trentrichardson.com/Impromptu/MIT-LICENSE.txt
+*
+* HERES THE CSS:
+* .ui-timepicker-div .ui-widget-header { margin-bottom: 8px; }
+* .ui-timepicker-div dl { text-align: left; }
+* .ui-timepicker-div dl dt { height: 25px; margin-bottom: -25px; }
+* .ui-timepicker-div dl dd { margin: 0 10px 10px 65px; }
+* .ui-timepicker-div td { font-size: 90%; }
+* .ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; }
+*/
+
+(function($) {
+
+// Prevent "Uncaught RangeError: Maximum call stack size exceeded"
+$.ui.timepicker = $.ui.timepicker || {};
+if ($.ui.timepicker.version) {
+ return;
+}
+
+$.extend($.ui, { timepicker: { version: "1.0.0" } });
+
+/* Time picker manager.
+ Use the singleton instance of this class, $.timepicker, to interact with the time picker.
+ Settings for (groups of) time pickers are maintained in an instance object,
+ allowing multiple different settings on the same page. */
+
+function Timepicker() {
+ this.regional = []; // Available regional settings, indexed by language code
+ this.regional[''] = { // Default regional settings
+ currentText: 'Now',
+ closeText: 'Done',
+ ampm: false,
+ amNames: ['AM', 'A'],
+ pmNames: ['PM', 'P'],
+ timeFormat: 'hh:mm tt',
+ timeSuffix: '',
+ timeOnlyTitle: 'Choose Time',
+ timeText: 'Time',
+ hourText: 'Hour',
+ minuteText: 'Minute',
+ secondText: 'Second',
+ millisecText: 'Millisecond',
+ timezoneText: 'Time Zone'
+ };
+ this._defaults = { // Global defaults for all the datetime picker instances
+ showButtonPanel: true,
+ timeOnly: false,
+ showHour: true,
+ showMinute: true,
+ showSecond: false,
+ showMillisec: false,
+ showTimezone: false,
+ showTime: true,
+ stepHour: 1,
+ stepMinute: 1,
+ stepSecond: 1,
+ stepMillisec: 1,
+ hour: 0,
+ minute: 0,
+ second: 0,
+ millisec: 0,
+ timezone: '+0000',
+ hourMin: 0,
+ minuteMin: 0,
+ secondMin: 0,
+ millisecMin: 0,
+ hourMax: 23,
+ minuteMax: 59,
+ secondMax: 59,
+ millisecMax: 999,
+ minDateTime: null,
+ maxDateTime: null,
+ onSelect: null,
+ hourGrid: 0,
+ minuteGrid: 0,
+ secondGrid: 0,
+ millisecGrid: 0,
+ alwaysSetTime: true,
+ separator: ' ',
+ altFieldTimeOnly: true,
+ showTimepicker: true,
+ timezoneIso8609: false,
+ timezoneList: null,
+ addSliderAccess: false,
+ sliderAccessArgs: null
+ };
+ $.extend(this._defaults, this.regional['']);
+};
+
+$.extend(Timepicker.prototype, {
+ $input: null,
+ $altInput: null,
+ $timeObj: null,
+ inst: null,
+ hour_slider: null,
+ minute_slider: null,
+ second_slider: null,
+ millisec_slider: null,
+ timezone_select: null,
+ hour: 0,
+ minute: 0,
+ second: 0,
+ millisec: 0,
+ timezone: '+0000',
+ hourMinOriginal: null,
+ minuteMinOriginal: null,
+ secondMinOriginal: null,
+ millisecMinOriginal: null,
+ hourMaxOriginal: null,
+ minuteMaxOriginal: null,
+ secondMaxOriginal: null,
+ millisecMaxOriginal: null,
+ ampm: '',
+ formattedDate: '',
+ formattedTime: '',
+ formattedDateTime: '',
+ timezoneList: null,
+
+ /* Override the default settings for all instances of the time picker.
+ @param settings object - the new settings to use as defaults (anonymous object)
+ @return the manager object */
+ setDefaults: function(settings) {
+ extendRemove(this._defaults, settings || {});
+ return this;
+ },
+
+ //########################################################################
+ // Create a new Timepicker instance
+ //########################################################################
+ _newInst: function($input, o) {
+ var tp_inst = new Timepicker(),
+ inlineSettings = {};
+
+ for (var attrName in this._defaults) {
+ var attrValue = $input.attr('time:' + attrName);
+ if (attrValue) {
+ try {
+ inlineSettings[attrName] = eval(attrValue);
+ } catch (err) {
+ inlineSettings[attrName] = attrValue;
+ }
+ }
+ }
+ tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, o, {
+ beforeShow: function(input, dp_inst) {
+ if ($.isFunction(o.beforeShow))
+ return o.beforeShow(input, dp_inst, tp_inst);
+ },
+ onChangeMonthYear: function(year, month, dp_inst) {
+ // Update the time as well : this prevents the time from disappearing from the $input field.
+ tp_inst._updateDateTime(dp_inst);
+ if ($.isFunction(o.onChangeMonthYear))
+ o.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);
+ },
+ onClose: function(dateText, dp_inst) {
+ if (tp_inst.timeDefined === true && $input.val() != '')
+ tp_inst._updateDateTime(dp_inst);
+ if ($.isFunction(o.onClose))
+ o.onClose.call($input[0], dateText, dp_inst, tp_inst);
+ },
+ timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');
+ });
+ tp_inst.amNames = $.map(tp_inst._defaults.amNames, function(val) { return val.toUpperCase(); });
+ tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function(val) { return val.toUpperCase(); });
+
+ if (tp_inst._defaults.timezoneList === null) {
+ var timezoneList = [];
+ for (var i = -11; i <= 12; i++)
+ timezoneList.push((i >= 0 ? '+' : '-') + ('0' + Math.abs(i).toString()).slice(-2) + '00');
+ if (tp_inst._defaults.timezoneIso8609)
+ timezoneList = $.map(timezoneList, function(val) {
+ return val == '+0000' ? 'Z' : (val.substring(0, 3) + ':' + val.substring(3));
+ });
+ tp_inst._defaults.timezoneList = timezoneList;
+ }
+
+ tp_inst.hour = tp_inst._defaults.hour;
+ tp_inst.minute = tp_inst._defaults.minute;
+ tp_inst.second = tp_inst._defaults.second;
+ tp_inst.millisec = tp_inst._defaults.millisec;
+ tp_inst.ampm = '';
+ tp_inst.$input = $input;
+
+ if (o.altField)
+ tp_inst.$altInput = $(o.altField)
+ .css({ cursor: 'pointer' })
+ .focus(function(){ $input.trigger("focus"); });
+
+ if(tp_inst._defaults.minDate==0 || tp_inst._defaults.minDateTime==0)
+ {
+ tp_inst._defaults.minDate=new Date();
+ }
+ if(tp_inst._defaults.maxDate==0 || tp_inst._defaults.maxDateTime==0)
+ {
+ tp_inst._defaults.maxDate=new Date();
+ }
+
+ // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..
+ if(tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date)
+ tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());
+ if(tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date)
+ tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());
+ if(tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date)
+ tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());
+ if(tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date)
+ tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());
+ return tp_inst;
+ },
+
+ //########################################################################
+ // add our sliders to the calendar
+ //########################################################################
+ _addTimePicker: function(dp_inst) {
+ var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ?
+ this.$input.val() + ' ' + this.$altInput.val() :
+ this.$input.val();
+
+ this.timeDefined = this._parseTime(currDT);
+ this._limitMinMaxDateTime(dp_inst, false);
+ this._injectTimePicker();
+ },
+
+ //########################################################################
+ // parse the time string from input value or _setTime
+ //########################################################################
+ _parseTime: function(timeString, withDate) {
+ var regstr = this._defaults.timeFormat.toString()
+ .replace(/h{1,2}/ig, '(\\d?\\d)')
+ .replace(/m{1,2}/ig, '(\\d?\\d)')
+ .replace(/s{1,2}/ig, '(\\d?\\d)')
+ .replace(/l{1}/ig, '(\\d?\\d?\\d)')
+ .replace(/t{1,2}/ig, this._getPatternAmpm())
+ .replace(/z{1}/ig, '(z|[-+]\\d\\d:?\\d\\d)?')
+ .replace(/\s/g, '\\s?') + this._defaults.timeSuffix + '$',
+ order = this._getFormatPositions(),
+ ampm = '',
+ treg;
+
+ if (!this.inst) this.inst = $.datepicker._getInst(this.$input[0]);
+
+ if (withDate || !this._defaults.timeOnly) {
+ // the time should come after x number of characters and a space.
+ // x = at least the length of text specified by the date format
+ var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');
+ // escape special regex characters in the seperator
+ var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g");
+ regstr = '^.{' + dp_dateFormat.length + ',}?' + this._defaults.separator.replace(specials, "\\$&") + regstr;
+ }
+
+ treg = timeString.match(new RegExp(regstr, 'i'));
+
+ if (treg) {
+ if (order.t !== -1) {
+ if (treg[order.t] === undefined || treg[order.t].length === 0) {
+ ampm = '';
+ this.ampm = '';
+ } else {
+ ampm = $.inArray(treg[order.t].toUpperCase(), this.amNames) !== -1 ? 'AM' : 'PM';
+ this.ampm = this._defaults[ampm == 'AM' ? 'amNames' : 'pmNames'][0];
+ }
+ }
+
+ if (order.h !== -1) {
+ if (ampm == 'AM' && treg[order.h] == '12')
+ this.hour = 0; // 12am = 0 hour
+ else if (ampm == 'PM' && treg[order.h] != '12')
+ this.hour = (parseFloat(treg[order.h]) + 12).toFixed(0); // 12pm = 12 hour, any other pm = hour + 12
+ else this.hour = Number(treg[order.h]);
+ }
+
+ if (order.m !== -1) this.minute = Number(treg[order.m]);
+ if (order.s !== -1) this.second = Number(treg[order.s]);
+ if (order.l !== -1) this.millisec = Number(treg[order.l]);
+ if (order.z !== -1 && treg[order.z] !== undefined) {
+ var tz = treg[order.z].toUpperCase();
+ switch (tz.length) {
+ case 1: // Z
+ tz = this._defaults.timezoneIso8609 ? 'Z' : '+0000';
+ break;
+ case 5: // +hhmm
+ if (this._defaults.timezoneIso8609)
+ tz = tz.substring(1) == '0000'
+ ? 'Z'
+ : tz.substring(0, 3) + ':' + tz.substring(3);
+ break;
+ case 6: // +hh:mm
+ if (!this._defaults.timezoneIso8609)
+ tz = tz == 'Z' || tz.substring(1) == '00:00'
+ ? '+0000'
+ : tz.replace(/:/, '');
+ else if (tz.substring(1) == '00:00')
+ tz = 'Z';
+ break;
+ }
+ this.timezone = tz;
+ }
+
+ return true;
+
+ }
+ return false;
+ },
+
+ //########################################################################
+ // pattern for standard and localized AM/PM markers
+ //########################################################################
+ _getPatternAmpm: function() {
+ var markers = [],
+ o = this._defaults;
+ if (o.amNames)
+ $.merge(markers, o.amNames);
+ if (o.pmNames)
+ $.merge(markers, o.pmNames);
+ markers = $.map(markers, function(val) { return val.replace(/[.*+?|()\[\]{}\\]/g, '\\$&'); });
+ return '(' + markers.join('|') + ')?';
+ },
+
+ //########################################################################
+ // figure out position of time elements.. cause js cant do named captures
+ //########################################################################
+ _getFormatPositions: function() {
+ var finds = this._defaults.timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|t{1,2}|z)/g),
+ orders = { h: -1, m: -1, s: -1, l: -1, t: -1, z: -1 };
+
+ if (finds)
+ for (var i = 0; i < finds.length; i++)
+ if (orders[finds[i].toString().charAt(0)] == -1)
+ orders[finds[i].toString().charAt(0)] = i + 1;
+
+ return orders;
+ },
+
+ //########################################################################
+ // generate and inject html for timepicker into ui datepicker
+ //########################################################################
+ _injectTimePicker: function() {
+ var $dp = this.inst.dpDiv,
+ o = this._defaults,
+ tp_inst = this,
+ // Added by Peter Medeiros:
+ // - Figure out what the hour/minute/second max should be based on the step values.
+ // - Example: if stepMinute is 15, then minMax is 45.
+ hourMax = parseInt((o.hourMax - ((o.hourMax - o.hourMin) % o.stepHour)) ,10),
+ minMax = parseInt((o.minuteMax - ((o.minuteMax - o.minuteMin) % o.stepMinute)) ,10),
+ secMax = parseInt((o.secondMax - ((o.secondMax - o.secondMin) % o.stepSecond)) ,10),
+ millisecMax = parseInt((o.millisecMax - ((o.millisecMax - o.millisecMin) % o.stepMillisec)) ,10),
+ dp_id = this.inst.id.toString().replace(/([^A-Za-z0-9_])/g, '');
+
+ // Prevent displaying twice
+ //if ($dp.find("div#ui-timepicker-div-"+ dp_id).length === 0) {
+ if ($dp.find("div#ui-timepicker-div-"+ dp_id).length === 0 && o.showTimepicker) {
+ var noDisplay = ' style="display:none;"',
+ html = '<div class="ui-timepicker-div" id="ui-timepicker-div-' + dp_id + '"><dl>' +
+ '<dt class="ui_tpicker_time_label" id="ui_tpicker_time_label_' + dp_id + '"' +
+ ((o.showTime) ? '' : noDisplay) + '>' + o.timeText + '</dt>' +
+ '<dd class="ui_tpicker_time" id="ui_tpicker_time_' + dp_id + '"' +
+ ((o.showTime) ? '' : noDisplay) + '></dd>' +
+ '<dt class="ui_tpicker_hour_label" id="ui_tpicker_hour_label_' + dp_id + '"' +
+ ((o.showHour) ? '' : noDisplay) + '>' + o.hourText + '</dt>',
+ hourGridSize = 0,
+ minuteGridSize = 0,
+ secondGridSize = 0,
+ millisecGridSize = 0,
+ size = null;
+
+ // Hours
+ html += '<dd class="ui_tpicker_hour"><div id="ui_tpicker_hour_' + dp_id + '"' +
+ ((o.showHour) ? '' : noDisplay) + '></div>';
+ if (o.showHour && o.hourGrid > 0) {
+ html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';
+
+ for (var h = o.hourMin; h <= hourMax; h += parseInt(o.hourGrid,10)) {
+ hourGridSize++;
+ var tmph = (o.ampm && h > 12) ? h-12 : h;
+ if (tmph < 10) tmph = '0' + tmph;
+ if (o.ampm) {
+ if (h == 0) tmph = 12 +'a';
+ else if (h < 12) tmph += 'a';
+ else tmph += 'p';
+ }
+ html += '<td>' + tmph + '</td>';
+ }
+
+ html += '</tr></table></div>';
+ }
+ html += '</dd>';
+
+ // Minutes
+ html += '<dt class="ui_tpicker_minute_label" id="ui_tpicker_minute_label_' + dp_id + '"' +
+ ((o.showMinute) ? '' : noDisplay) + '>' + o.minuteText + '</dt>'+
+ '<dd class="ui_tpicker_minute"><div id="ui_tpicker_minute_' + dp_id + '"' +
+ ((o.showMinute) ? '' : noDisplay) + '></div>';
+
+ if (o.showMinute && o.minuteGrid > 0) {
+ html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';
+
+ for (var m = o.minuteMin; m <= minMax; m += parseInt(o.minuteGrid,10)) {
+ minuteGridSize++;
+ html += '<td>' + ((m < 10) ? '0' : '') + m + '</td>';
+ }
+
+ html += '</tr></table></div>';
+ }
+ html += '</dd>';
+
+ // Seconds
+ html += '<dt class="ui_tpicker_second_label" id="ui_tpicker_second_label_' + dp_id + '"' +
+ ((o.showSecond) ? '' : noDisplay) + '>' + o.secondText + '</dt>'+
+ '<dd class="ui_tpicker_second"><div id="ui_tpicker_second_' + dp_id + '"'+
+ ((o.showSecond) ? '' : noDisplay) + '></div>';
+
+ if (o.showSecond && o.secondGrid > 0) {
+ html += '<div style="padding-left: 1px"><table><tr>';
+
+ for (var s = o.secondMin; s <= secMax; s += parseInt(o.secondGrid,10)) {
+ secondGridSize++;
+ html += '<td>' + ((s < 10) ? '0' : '') + s + '</td>';
+ }
+
+ html += '</tr></table></div>';
+ }
+ html += '</dd>';
+
+ // Milliseconds
+ html += '<dt class="ui_tpicker_millisec_label" id="ui_tpicker_millisec_label_' + dp_id + '"' +
+ ((o.showMillisec) ? '' : noDisplay) + '>' + o.millisecText + '</dt>'+
+ '<dd class="ui_tpicker_millisec"><div id="ui_tpicker_millisec_' + dp_id + '"'+
+ ((o.showMillisec) ? '' : noDisplay) + '></div>';
+
+ if (o.showMillisec && o.millisecGrid > 0) {
+ html += '<div style="padding-left: 1px"><table><tr>';
+
+ for (var l = o.millisecMin; l <= millisecMax; l += parseInt(o.millisecGrid,10)) {
+ millisecGridSize++;
+ html += '<td>' + ((l < 10) ? '0' : '') + l + '</td>';
+ }
+
+ html += '</tr></table></div>';
+ }
+ html += '</dd>';
+
+ // Timezone
+ html += '<dt class="ui_tpicker_timezone_label" id="ui_tpicker_timezone_label_' + dp_id + '"' +
+ ((o.showTimezone) ? '' : noDisplay) + '>' + o.timezoneText + '</dt>';
+ html += '<dd class="ui_tpicker_timezone" id="ui_tpicker_timezone_' + dp_id + '"' +
+ ((o.showTimezone) ? '' : noDisplay) + '></dd>';
+
+ html += '</dl></div>';
+ $tp = $(html);
+
+ // if we only want time picker...
+ if (o.timeOnly === true) {
+ $tp.prepend(
+ '<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' +
+ '<div class="ui-datepicker-title">' + o.timeOnlyTitle + '</div>' +
+ '</div>');
+ $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();
+ }
+
+ this.hour_slider = $tp.find('#ui_tpicker_hour_'+ dp_id).slider({
+ orientation: "horizontal",
+ value: this.hour,
+ min: o.hourMin,
+ max: hourMax,
+ step: o.stepHour,
+ slide: function(event, ui) {
+ tp_inst.hour_slider.slider( "option", "value", ui.value);
+ tp_inst._onTimeChange();
+ }
+ });
+
+
+ // Updated by Peter Medeiros:
+ // - Pass in Event and UI instance into slide function
+ this.minute_slider = $tp.find('#ui_tpicker_minute_'+ dp_id).slider({
+ orientation: "horizontal",
+ value: this.minute,
+ min: o.minuteMin,
+ max: minMax,
+ step: o.stepMinute,
+ slide: function(event, ui) {
+ tp_inst.minute_slider.slider( "option", "value", ui.value);
+ tp_inst._onTimeChange();
+ }
+ });
+
+ this.second_slider = $tp.find('#ui_tpicker_second_'+ dp_id).slider({
+ orientation: "horizontal",
+ value: this.second,
+ min: o.secondMin,
+ max: secMax,
+ step: o.stepSecond,
+ slide: function(event, ui) {
+ tp_inst.second_slider.slider( "option", "value", ui.value);
+ tp_inst._onTimeChange();
+ }
+ });
+
+ this.millisec_slider = $tp.find('#ui_tpicker_millisec_'+ dp_id).slider({
+ orientation: "horizontal",
+ value: this.millisec,
+ min: o.millisecMin,
+ max: millisecMax,
+ step: o.stepMillisec,
+ slide: function(event, ui) {
+ tp_inst.millisec_slider.slider( "option", "value", ui.value);
+ tp_inst._onTimeChange();
+ }
+ });
+
+ this.timezone_select = $tp.find('#ui_tpicker_timezone_'+ dp_id).append('<select></select>').find("select");
+ $.fn.append.apply(this.timezone_select,
+ $.map(o.timezoneList, function(val, idx) {
+ return $("<option />")
+ .val(typeof val == "object" ? val.value : val)
+ .text(typeof val == "object" ? val.label : val);
+ })
+ );
+ this.timezone_select.val((typeof this.timezone != "undefined" && this.timezone != null && this.timezone != "") ? this.timezone : o.timezone);
+ this.timezone_select.change(function() {
+ tp_inst._onTimeChange();
+ });
+
+ // Add grid functionality
+ if (o.showHour && o.hourGrid > 0) {
+ size = 100 * hourGridSize * o.hourGrid / (hourMax - o.hourMin);
+
+ $tp.find(".ui_tpicker_hour table").css({
+ width: size + "%",
+ marginLeft: (size / (-2 * hourGridSize)) + "%",
+ borderCollapse: 'collapse'
+ }).find("td").each( function(index) {
+ $(this).click(function() {
+ var h = $(this).html();
+ if(o.ampm) {
+ var ap = h.substring(2).toLowerCase(),
+ aph = parseInt(h.substring(0,2), 10);
+ if (ap == 'a') {
+ if (aph == 12) h = 0;
+ else h = aph;
+ } else if (aph == 12) h = 12;
+ else h = aph + 12;
+ }
+ tp_inst.hour_slider.slider("option", "value", h);
+ tp_inst._onTimeChange();
+ tp_inst._onSelectHandler();
+ }).css({
+ cursor: 'pointer',
+ width: (100 / hourGridSize) + '%',
+ textAlign: 'center',
+ overflow: 'hidden'
+ });
+ });
+ }
+
+ if (o.showMinute && o.minuteGrid > 0) {
+ size = 100 * minuteGridSize * o.minuteGrid / (minMax - o.minuteMin);
+ $tp.find(".ui_tpicker_minute table").css({
+ width: size + "%",
+ marginLeft: (size / (-2 * minuteGridSize)) + "%",
+ borderCollapse: 'collapse'
+ }).find("td").each(function(index) {
+ $(this).click(function() {
+ tp_inst.minute_slider.slider("option", "value", $(this).html());
+ tp_inst._onTimeChange();
+ tp_inst._onSelectHandler();
+ }).css({
+ cursor: 'pointer',
+ width: (100 / minuteGridSize) + '%',
+ textAlign: 'center',
+ overflow: 'hidden'
+ });
+ });
+ }
+
+ if (o.showSecond && o.secondGrid > 0) {
+ $tp.find(".ui_tpicker_second table").css({
+ width: size + "%",
+ marginLeft: (size / (-2 * secondGridSize)) + "%",
+ borderCollapse: 'collapse'
+ }).find("td").each(function(index) {
+ $(this).click(function() {
+ tp_inst.second_slider.slider("option", "value", $(this).html());
+ tp_inst._onTimeChange();
+ tp_inst._onSelectHandler();
+ }).css({
+ cursor: 'pointer',
+ width: (100 / secondGridSize) + '%',
+ textAlign: 'center',
+ overflow: 'hidden'
+ });
+ });
+ }
+
+ if (o.showMillisec && o.millisecGrid > 0) {
+ $tp.find(".ui_tpicker_millisec table").css({
+ width: size + "%",
+ marginLeft: (size / (-2 * millisecGridSize)) + "%",
+ borderCollapse: 'collapse'
+ }).find("td").each(function(index) {
+ $(this).click(function() {
+ tp_inst.millisec_slider.slider("option", "value", $(this).html());
+ tp_inst._onTimeChange();
+ tp_inst._onSelectHandler();
+ }).css({
+ cursor: 'pointer',
+ width: (100 / millisecGridSize) + '%',
+ textAlign: 'center',
+ overflow: 'hidden'
+ });
+ });
+ }
+
+ var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');
+ if ($buttonPanel.length) $buttonPanel.before($tp);
+ else $dp.append($tp);
+
+ this.$timeObj = $tp.find('#ui_tpicker_time_'+ dp_id);
+
+ if (this.inst !== null) {
+ var timeDefined = this.timeDefined;
+ this._onTimeChange();
+ this.timeDefined = timeDefined;
+ }
+
+ //Emulate datepicker onSelect behavior. Call on slidestop.
+ var onSelectDelegate = function() {
+ tp_inst._onSelectHandler();
+ };
+ this.hour_slider.bind('slidestop',onSelectDelegate);
+ this.minute_slider.bind('slidestop',onSelectDelegate);
+ this.second_slider.bind('slidestop',onSelectDelegate);
+ this.millisec_slider.bind('slidestop',onSelectDelegate);
+
+ // slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/
+ if (this._defaults.addSliderAccess){
+ var sliderAccessArgs = this._defaults.sliderAccessArgs;
+ setTimeout(function(){ // fix for inline mode
+ if($tp.find('.ui-slider-access').length == 0){
+ $tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);
+
+ // fix any grids since sliders are shorter
+ var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);
+ if(sliderAccessWidth){
+ $tp.find('table:visible').each(function(){
+ var $g = $(this),
+ oldWidth = $g.outerWidth(),
+ oldMarginLeft = $g.css('marginLeft').toString().replace('%',''),
+ newWidth = oldWidth - sliderAccessWidth,
+ newMarginLeft = ((oldMarginLeft * newWidth)/oldWidth) + '%';
+
+ $g.css({ width: newWidth, marginLeft: newMarginLeft });
+ });
+ }
+ }
+ },0);
+ }
+ // end slideAccess integration
+
+ }
+ },
+
+ //########################################################################
+ // This function tries to limit the ability to go outside the
+ // min/max date range
+ //########################################################################
+ _limitMinMaxDateTime: function(dp_inst, adjustSliders){
+ var o = this._defaults,
+ dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);
+
+ if(!this._defaults.showTimepicker) return; // No time so nothing to check here
+
+ if($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date){
+ var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),
+ minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);
+
+ if(this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null){
+ this.hourMinOriginal = o.hourMin;
+ this.minuteMinOriginal = o.minuteMin;
+ this.secondMinOriginal = o.secondMin;
+ this.millisecMinOriginal = o.millisecMin;
+ }
+
+ if(dp_inst.settings.timeOnly || minDateTimeDate.getTime() == dp_date.getTime()) {
+ this._defaults.hourMin = minDateTime.getHours();
+ if (this.hour <= this._defaults.hourMin) {
+ this.hour = this._defaults.hourMin;
+ this._defaults.minuteMin = minDateTime.getMinutes();
+ if (this.minute <= this._defaults.minuteMin) {
+ this.minute = this._defaults.minuteMin;
+ this._defaults.secondMin = minDateTime.getSeconds();
+ } else if (this.second <= this._defaults.secondMin){
+ this.second = this._defaults.secondMin;
+ this._defaults.millisecMin = minDateTime.getMilliseconds();
+ } else {
+ if(this.millisec < this._defaults.millisecMin)
+ this.millisec = this._defaults.millisecMin;
+ this._defaults.millisecMin = this.millisecMinOriginal;
+ }
+ } else {
+ this._defaults.minuteMin = this.minuteMinOriginal;
+ this._defaults.secondMin = this.secondMinOriginal;
+ this._defaults.millisecMin = this.millisecMinOriginal;
+ }
+ }else{
+ this._defaults.hourMin = this.hourMinOriginal;
+ this._defaults.minuteMin = this.minuteMinOriginal;
+ this._defaults.secondMin = this.secondMinOriginal;
+ this._defaults.millisecMin = this.millisecMinOriginal;
+ }
+ }
+
+ if($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date){
+ var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),
+ maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);
+
+ if(this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null){
+ this.hourMaxOriginal = o.hourMax;
+ this.minuteMaxOriginal = o.minuteMax;
+ this.secondMaxOriginal = o.secondMax;
+ this.millisecMaxOriginal = o.millisecMax;
+ }
+
+ if(dp_inst.settings.timeOnly || maxDateTimeDate.getTime() == dp_date.getTime()){
+ this._defaults.hourMax = maxDateTime.getHours();
+ if (this.hour >= this._defaults.hourMax) {
+ this.hour = this._defaults.hourMax;
+ this._defaults.minuteMax = maxDateTime.getMinutes();
+ if (this.minute >= this._defaults.minuteMax) {
+ this.minute = this._defaults.minuteMax;
+ this._defaults.secondMax = maxDateTime.getSeconds();
+ } else if (this.second >= this._defaults.secondMax) {
+ this.second = this._defaults.secondMax;
+ this._defaults.millisecMax = maxDateTime.getMilliseconds();
+ } else {
+ if(this.millisec > this._defaults.millisecMax) this.millisec = this._defaults.millisecMax;
+ this._defaults.millisecMax = this.millisecMaxOriginal;
+ }
+ } else {
+ this._defaults.minuteMax = this.minuteMaxOriginal;
+ this._defaults.secondMax = this.secondMaxOriginal;
+ this._defaults.millisecMax = this.millisecMaxOriginal;
+ }
+ }else{
+ this._defaults.hourMax = this.hourMaxOriginal;
+ this._defaults.minuteMax = this.minuteMaxOriginal;
+ this._defaults.secondMax = this.secondMaxOriginal;
+ this._defaults.millisecMax = this.millisecMaxOriginal;
+ }
+ }
+
+ if(adjustSliders !== undefined && adjustSliders === true){
+ var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)) ,10),
+ minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)) ,10),
+ secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)) ,10),
+ millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)) ,10);
+
+ if(this.hour_slider)
+ this.hour_slider.slider("option", { min: this._defaults.hourMin, max: hourMax }).slider('value', this.hour);
+ if(this.minute_slider)
+ this.minute_slider.slider("option", { min: this._defaults.minuteMin, max: minMax }).slider('value', this.minute);
+ if(this.second_slider)
+ this.second_slider.slider("option", { min: this._defaults.secondMin, max: secMax }).slider('value', this.second);
+ if(this.millisec_slider)
+ this.millisec_slider.slider("option", { min: this._defaults.millisecMin, max: millisecMax }).slider('value', this.millisec);
+ }
+
+ },
+
+
+ //########################################################################
+ // when a slider moves, set the internal time...
+ // on time change is also called when the time is updated in the text field
+ //########################################################################
+ _onTimeChange: function() {
+ var hour = (this.hour_slider) ? this.hour_slider.slider('value') : false,
+ minute = (this.minute_slider) ? this.minute_slider.slider('value') : false,
+ second = (this.second_slider) ? this.second_slider.slider('value') : false,
+ millisec = (this.millisec_slider) ? this.millisec_slider.slider('value') : false,
+ timezone = (this.timezone_select) ? this.timezone_select.val() : false,
+ o = this._defaults;
+
+ if (typeof(hour) == 'object') hour = false;
+ if (typeof(minute) == 'object') minute = false;
+ if (typeof(second) == 'object') second = false;
+ if (typeof(millisec) == 'object') millisec = false;
+ if (typeof(timezone) == 'object') timezone = false;
+
+ if (hour !== false) hour = parseInt(hour,10);
+ if (minute !== false) minute = parseInt(minute,10);
+ if (second !== false) second = parseInt(second,10);
+ if (millisec !== false) millisec = parseInt(millisec,10);
+
+ var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];
+
+ // If the update was done in the input field, the input field should not be updated.
+ // If the update was done using the sliders, update the input field.
+ var hasChanged = (hour != this.hour || minute != this.minute
+ || second != this.second || millisec != this.millisec
+ || (this.ampm.length > 0
+ && (hour < 12) != ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1))
+ || timezone != this.timezone);
+
+ if (hasChanged) {
+
+ if (hour !== false)this.hour = hour;
+ if (minute !== false) this.minute = minute;
+ if (second !== false) this.second = second;
+ if (millisec !== false) this.millisec = millisec;
+ if (timezone !== false) this.timezone = timezone;
+
+ if (!this.inst) this.inst = $.datepicker._getInst(this.$input[0]);
+
+ this._limitMinMaxDateTime(this.inst, true);
+ }
+ if (o.ampm) this.ampm = ampm;
+
+ //this._formatTime();
+ this.formattedTime = $.datepicker.formatTime(this._defaults.timeFormat, this, this._defaults);
+ if (this.$timeObj) this.$timeObj.text(this.formattedTime + o.timeSuffix);
+ this.timeDefined = true;
+ if (hasChanged) this._updateDateTime();
+ },
+
+ //########################################################################
+ // call custom onSelect.
+ // bind to sliders slidestop, and grid click.
+ //########################################################################
+ _onSelectHandler: function() {
+ var onSelect = this._defaults.onSelect;
+ var inputEl = this.$input ? this.$input[0] : null;
+ if (onSelect && inputEl) {
+ onSelect.apply(inputEl, [this.formattedDateTime, this]);
+ }
+ },
+
+ //########################################################################
+ // left for any backwards compatibility
+ //########################################################################
+ _formatTime: function(time, format) {
+ time = time || { hour: this.hour, minute: this.minute, second: this.second, millisec: this.millisec, ampm: this.ampm, timezone: this.timezone };
+ var tmptime = (format || this._defaults.timeFormat).toString();
+
+ tmptime = $.datepicker.formatTime(tmptime, time, this._defaults);
+
+ if (arguments.length) return tmptime;
+ else this.formattedTime = tmptime;
+ },
+
+ //########################################################################
+ // update our input with the new date time..
+ //########################################################################
+ _updateDateTime: function(dp_inst) {
+ dp_inst = this.inst || dp_inst;
+ var dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
+ dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),
+ formatCfg = $.datepicker._getFormatConfig(dp_inst),
+ timeAvailable = dt !== null && this.timeDefined;
+ this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);
+ var formattedDateTime = this.formattedDate;
+ if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0))
+ return;
+
+ if (this._defaults.timeOnly === true) {
+ formattedDateTime = this.formattedTime;
+ } else if (this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) {
+ formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;
+ }
+
+ this.formattedDateTime = formattedDateTime;
+
+ if(!this._defaults.showTimepicker) {
+ this.$input.val(this.formattedDate);
+ } else if (this.$altInput && this._defaults.altFieldTimeOnly === true) {
+ this.$altInput.val(this.formattedTime);
+ this.$input.val(this.formattedDate);
+ } else if(this.$altInput) {
+ this.$altInput.val(formattedDateTime);
+ this.$input.val(formattedDateTime);
+ } else {
+ this.$input.val(formattedDateTime);
+ }
+
+ this.$input.trigger("change");
+ }
+
+});
+
+$.fn.extend({
+ //########################################################################
+ // shorthand just to use timepicker..
+ //########################################################################
+ timepicker: function(o) {
+ o = o || {};
+ var tmp_args = arguments;
+
+ if (typeof o == 'object') tmp_args[0] = $.extend(o, { timeOnly: true });
+
+ return $(this).each(function() {
+ $.fn.datetimepicker.apply($(this), tmp_args);
+ });
+ },
+
+ //########################################################################
+ // extend timepicker to datepicker
+ //########################################################################
+ datetimepicker: function(o) {
+ o = o || {};
+ tmp_args = arguments;
+
+ if (typeof(o) == 'string'){
+ if(o == 'getDate')
+ return $.fn.datepicker.apply($(this[0]), tmp_args);
+ else
+ return this.each(function() {
+ var $t = $(this);
+ $t.datepicker.apply($t, tmp_args);
+ });
+ }
+ else
+ return this.each(function() {
+ var $t = $(this);
+ $t.datepicker($.timepicker._newInst($t, o)._defaults);
+ });
+ }
+});
+
+//########################################################################
+// format the time all pretty...
+// format = string format of the time
+// time = a {}, not a Date() for timezones
+// options = essentially the regional[].. amNames, pmNames, ampm
+//########################################################################
+$.datepicker.formatTime = function(format, time, options) {
+ options = options || {};
+ options = $.extend($.timepicker._defaults, options);
+ time = $.extend({hour:0, minute:0, second:0, millisec:0, timezone:'+0000'}, time);
+
+ var tmptime = format;
+ var ampmName = options['amNames'][0];
+
+ var hour = parseInt(time.hour, 10);
+ if (options.ampm) {
+ if (hour > 11){
+ ampmName = options['pmNames'][0];
+ if(hour > 12)
+ hour = hour % 12;
+ }
+ if (hour === 0)
+ hour = 12;
+ }
+ tmptime = tmptime.replace(/(?:hh?|mm?|ss?|[tT]{1,2}|[lz])/g, function(match) {
+ switch (match.toLowerCase()) {
+ case 'hh': return ('0' + hour).slice(-2);
+ case 'h': return hour;
+ case 'mm': return ('0' + time.minute).slice(-2);
+ case 'm': return time.minute;
+ case 'ss': return ('0' + time.second).slice(-2);
+ case 's': return time.second;
+ case 'l': return ('00' + time.millisec).slice(-3);
+ case 'z': return time.timezone;
+ case 't': case 'tt':
+ if (options.ampm) {
+ if (match.length == 1)
+ ampmName = ampmName.charAt(0);
+ return match.charAt(0) == 'T' ? ampmName.toUpperCase() : ampmName.toLowerCase();
+ }
+ return '';
+ }
+ });
+
+ tmptime = $.trim(tmptime);
+ return tmptime;
+};
+
+//########################################################################
+// the bad hack :/ override datepicker so it doesnt close on select
+// inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378
+//########################################################################
+$.datepicker._base_selectDate = $.datepicker._selectDate;
+$.datepicker._selectDate = function (id, dateStr) {
+ var inst = this._getInst($(id)[0]),
+ tp_inst = this._get(inst, 'timepicker');
+
+ if (tp_inst) {
+ tp_inst._limitMinMaxDateTime(inst, true);
+ inst.inline = inst.stay_open = true;
+ //This way the onSelect handler called from calendarpicker get the full dateTime
+ this._base_selectDate(id, dateStr);
+ inst.inline = inst.stay_open = false;
+ this._notifyChange(inst);
+ this._updateDatepicker(inst);
+ }
+ else this._base_selectDate(id, dateStr);
+};
+
+//#############################################################################################
+// second bad hack :/ override datepicker so it triggers an event when changing the input field
+// and does not redraw the datepicker on every selectDate event
+//#############################################################################################
+$.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;
+$.datepicker._updateDatepicker = function(inst) {
+
+ // don't popup the datepicker if there is another instance already opened
+ var input = inst.input[0];
+ if($.datepicker._curInst &&
+ $.datepicker._curInst != inst &&
+ $.datepicker._datepickerShowing &&
+ $.datepicker._lastInput != input) {
+ return;
+ }
+
+ if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {
+
+ this._base_updateDatepicker(inst);
+
+ // Reload the time control when changing something in the input text field.
+ var tp_inst = this._get(inst, 'timepicker');
+ if(tp_inst) tp_inst._addTimePicker(inst);
+ }
+};
+
+//#######################################################################################
+// third bad hack :/ override datepicker so it allows spaces and colon in the input field
+//#######################################################################################
+$.datepicker._base_doKeyPress = $.datepicker._doKeyPress;
+$.datepicker._doKeyPress = function(event) {
+ var inst = $.datepicker._getInst(event.target),
+ tp_inst = $.datepicker._get(inst, 'timepicker');
+
+ if (tp_inst) {
+ if ($.datepicker._get(inst, 'constrainInput')) {
+ var ampm = tp_inst._defaults.ampm,
+ dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),
+ datetimeChars = tp_inst._defaults.timeFormat.toString()
+ .replace(/[hms]/g, '')
+ .replace(/TT/g, ampm ? 'APM' : '')
+ .replace(/Tt/g, ampm ? 'AaPpMm' : '')
+ .replace(/tT/g, ampm ? 'AaPpMm' : '')
+ .replace(/T/g, ampm ? 'AP' : '')
+ .replace(/tt/g, ampm ? 'apm' : '')
+ .replace(/t/g, ampm ? 'ap' : '') +
+ " " +
+ tp_inst._defaults.separator +
+ tp_inst._defaults.timeSuffix +
+ (tp_inst._defaults.showTimezone ? tp_inst._defaults.timezoneList.join('') : '') +
+ (tp_inst._defaults.amNames.join('')) +
+ (tp_inst._defaults.pmNames.join('')) +
+ dateChars,
+ chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);
+ return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);
+ }
+ }
+
+ return $.datepicker._base_doKeyPress(event);
+};
+
+//#######################################################################################
+// Override key up event to sync manual input changes.
+//#######################################################################################
+$.datepicker._base_doKeyUp = $.datepicker._doKeyUp;
+$.datepicker._doKeyUp = function (event) {
+ var inst = $.datepicker._getInst(event.target),
+ tp_inst = $.datepicker._get(inst, 'timepicker');
+
+ if (tp_inst) {
+ if (tp_inst._defaults.timeOnly && (inst.input.val() != inst.lastVal)) {
+ try {
+ $.datepicker._updateDatepicker(inst);
+ }
+ catch (err) {
+ $.datepicker.log(err);
+ }
+ }
+ }
+
+ return $.datepicker._base_doKeyUp(event);
+};
+
+//#######################################################################################
+// override "Today" button to also grab the time.
+//#######################################################################################
+$.datepicker._base_gotoToday = $.datepicker._gotoToday;
+$.datepicker._gotoToday = function(id) {
+ var inst = this._getInst($(id)[0]),
+ $dp = inst.dpDiv;
+ this._base_gotoToday(id);
+ var now = new Date();
+ var tp_inst = this._get(inst, 'timepicker');
+ if (tp_inst && tp_inst._defaults.showTimezone && tp_inst.timezone_select) {
+ var tzoffset = now.getTimezoneOffset(); // If +0100, returns -60
+ var tzsign = tzoffset > 0 ? '-' : '+';
+ tzoffset = Math.abs(tzoffset);
+ var tzmin = tzoffset % 60;
+ tzoffset = tzsign + ('0' + (tzoffset - tzmin) / 60).slice(-2) + ('0' + tzmin).slice(-2);
+ if (tp_inst._defaults.timezoneIso8609)
+ tzoffset = tzoffset.substring(0, 3) + ':' + tzoffset.substring(3);
+ tp_inst.timezone_select.val(tzoffset);
+ }
+ this._setTime(inst, now);
+ $( '.ui-datepicker-today', $dp).click();
+};
+
+//#######################################################################################
+// Disable & enable the Time in the datetimepicker
+//#######################################################################################
+$.datepicker._disableTimepickerDatepicker = function(target, date, withDate) {
+ var inst = this._getInst(target),
+ tp_inst = this._get(inst, 'timepicker');
+ $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
+ if (tp_inst) {
+ tp_inst._defaults.showTimepicker = false;
+ tp_inst._updateDateTime(inst);
+ }
+};
+
+$.datepicker._enableTimepickerDatepicker = function(target, date, withDate) {
+ var inst = this._getInst(target),
+ tp_inst = this._get(inst, 'timepicker');
+ $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
+ if (tp_inst) {
+ tp_inst._defaults.showTimepicker = true;
+ tp_inst._addTimePicker(inst); // Could be disabled on page load
+ tp_inst._updateDateTime(inst);
+ }
+};
+
+//#######################################################################################
+// Create our own set time function
+//#######################################################################################
+$.datepicker._setTime = function(inst, date) {
+ var tp_inst = this._get(inst, 'timepicker');
+ if (tp_inst) {
+ var defaults = tp_inst._defaults,
+ // calling _setTime with no date sets time to defaults
+ hour = date ? date.getHours() : defaults.hour,
+ minute = date ? date.getMinutes() : defaults.minute,
+ second = date ? date.getSeconds() : defaults.second,
+ millisec = date ? date.getMilliseconds() : defaults.millisec;
+
+ //check if within min/max times..
+ if ((hour < defaults.hourMin || hour > defaults.hourMax) || (minute < defaults.minuteMin || minute > defaults.minuteMax) || (second < defaults.secondMin || second > defaults.secondMax) || (millisec < defaults.millisecMin || millisec > defaults.millisecMax)) {
+ hour = defaults.hourMin;
+ minute = defaults.minuteMin;
+ second = defaults.secondMin;
+ millisec = defaults.millisecMin;
+ }
+
+ tp_inst.hour = hour;
+ tp_inst.minute = minute;
+ tp_inst.second = second;
+ tp_inst.millisec = millisec;
+
+ if (tp_inst.hour_slider) tp_inst.hour_slider.slider('value', hour);
+ if (tp_inst.minute_slider) tp_inst.minute_slider.slider('value', minute);
+ if (tp_inst.second_slider) tp_inst.second_slider.slider('value', second);
+ if (tp_inst.millisec_slider) tp_inst.millisec_slider.slider('value', millisec);
+
+ tp_inst._onTimeChange();
+ tp_inst._updateDateTime(inst);
+ }
+};
+
+//#######################################################################################
+// Create new public method to set only time, callable as $().datepicker('setTime', date)
+//#######################################################################################
+$.datepicker._setTimeDatepicker = function(target, date, withDate) {
+ var inst = this._getInst(target),
+ tp_inst = this._get(inst, 'timepicker');
+
+ if (tp_inst) {
+ this._setDateFromField(inst);
+ var tp_date;
+ if (date) {
+ if (typeof date == "string") {
+ tp_inst._parseTime(date, withDate);
+ tp_date = new Date();
+ tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
+ }
+ else tp_date = new Date(date.getTime());
+ if (tp_date.toString() == 'Invalid Date') tp_date = undefined;
+ this._setTime(inst, tp_date);
+ }
+ }
+
+};
+
+//#######################################################################################
+// override setDate() to allow setting time too within Date object
+//#######################################################################################
+$.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;
+$.datepicker._setDateDatepicker = function(target, date) {
+ var inst = this._getInst(target),
+ tp_date = (date instanceof Date) ? new Date(date.getTime()) : date;
+
+ this._updateDatepicker(inst);
+ this._base_setDateDatepicker.apply(this, arguments);
+ this._setTimeDatepicker(target, tp_date, true);
+};
+
+//#######################################################################################
+// override getDate() to allow getting time too within Date object
+//#######################################################################################
+$.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;
+$.datepicker._getDateDatepicker = function(target, noDefault) {
+ var inst = this._getInst(target),
+ tp_inst = this._get(inst, 'timepicker');
+
+ if (tp_inst) {
+ this._setDateFromField(inst, noDefault);
+ var date = this._getDate(inst);
+ if (date && tp_inst._parseTime($(target).val(), tp_inst.timeOnly)) date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
+ return date;
+ }
+ return this._base_getDateDatepicker(target, noDefault);
+};
+
+//#######################################################################################
+// override parseDate() because UI 1.8.14 throws an error about "Extra characters"
+// An option in datapicker to ignore extra format characters would be nicer.
+//#######################################################################################
+$.datepicker._base_parseDate = $.datepicker.parseDate;
+$.datepicker.parseDate = function(format, value, settings) {
+ var date;
+ try {
+ date = this._base_parseDate(format, value, settings);
+ } catch (err) {
+ if (err.indexOf(":") >= 0) {
+ // Hack! The error message ends with a colon, a space, and
+ // the "extra" characters. We rely on that instead of
+ // attempting to perfectly reproduce the parsing algorithm.
+ date = this._base_parseDate(format, value.substring(0,value.length-(err.length-err.indexOf(':')-2)), settings);
+ } else {
+ // The underlying error was not related to the time
+ throw err;
+ }
+ }
+ return date;
+};
+
+//#######################################################################################
+// override formatDate to set date with time to the input
+//#######################################################################################
+$.datepicker._base_formatDate = $.datepicker._formatDate;
+$.datepicker._formatDate = function(inst, day, month, year){
+ var tp_inst = this._get(inst, 'timepicker');
+ if(tp_inst) {
+ tp_inst._updateDateTime(inst);
+ return tp_inst.$input.val();
+ }
+ return this._base_formatDate(inst);
+};
+
+//#######################################################################################
+// override options setter to add time to maxDate(Time) and minDate(Time). MaxDate
+//#######################################################################################
+$.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;
+$.datepicker._optionDatepicker = function(target, name, value) {
+ var inst = this._getInst(target),
+ tp_inst = this._get(inst, 'timepicker');
+ if (tp_inst) {
+ var min = null, max = null, onselect = null;
+ if (typeof name == 'string') { // if min/max was set with the string
+ if (name === 'minDate' || name === 'minDateTime' )
+ min = value;
+ else if (name === 'maxDate' || name === 'maxDateTime')
+ max = value;
+ else if (name === 'onSelect')
+ onselect = value;
+ } else if (typeof name == 'object') { //if min/max was set with the JSON
+ if (name.minDate)
+ min = name.minDate;
+ else if (name.minDateTime)
+ min = name.minDateTime;
+ else if (name.maxDate)
+ max = name.maxDate;
+ else if (name.maxDateTime)
+ max = name.maxDateTime;
+ }
+ if(min) { //if min was set
+ if (min == 0)
+ min = new Date();
+ else
+ min = new Date(min);
+
+ tp_inst._defaults.minDate = min;
+ tp_inst._defaults.minDateTime = min;
+ } else if (max) { //if max was set
+ if(max==0)
+ max=new Date();
+ else
+ max= new Date(max);
+ tp_inst._defaults.maxDate = max;
+ tp_inst._defaults.maxDateTime = max;
+ } else if (onselect)
+ tp_inst._defaults.onSelect = onselect;
+ }
+ if (value === undefined)
+ return this._base_optionDatepicker(target, name);
+ return this._base_optionDatepicker(target, name, value);
+};
+
+//#######################################################################################
+// jQuery extend now ignores nulls!
+//#######################################################################################
+function extendRemove(target, props) {
+ $.extend(target, props);
+ for (var name in props)
+ if (props[name] === null || props[name] === undefined)
+ target[name] = props[name];
+ return target;
+};
+
+$.timepicker = new Timepicker(); // singleton instance
+$.timepicker.version = "1.0.0";
+
+})(jQuery);
diff --git a/rt/share/html/NoAuth/js/ui.timepickr.js b/rt/share/html/NoAuth/js/ui.timepickr.js
deleted file mode 100644
index 3b2040a21..000000000
--- a/rt/share/html/NoAuth/js/ui.timepickr.js
+++ /dev/null
@@ -1,941 +0,0 @@
-/*
- jQuery utils - @VERSION
- http://code.google.com/p/jquery-utils/
-
- (c) Maxime Haineault <haineault@gmail.com>
- http://haineault.com
-
- MIT License (http://www.opensource.org/licenses/mit-license.php
-
-*/
-
-(function($){
- $.extend($.expr[':'], {
- // case insensitive version of :contains
- icontains: function(a,i,m){return (a.textContent||a.innerText||jQuery(a).text()||"").toLowerCase().indexOf(m[3].toLowerCase())>=0;}
- });
-
- $.iterators = {
- getText: function() { return $(this).text(); },
- parseInt: function(v){ return parseInt(v, 10); }
- };
-
- $.extend({
-
- // Returns a range object
- // Author: Matthias Miller
- // Site: http://blog.outofhanwell.com/2006/03/29/javascript-range-function/
- range: function() {
- if (!arguments.length) { return []; }
- var min, max, step;
- if (arguments.length == 1) {
- min = 0;
- max = arguments[0]-1;
- step = 1;
- }
- else {
- // default step to 1 if it's zero or undefined
- min = arguments[0];
- max = arguments[1]-1;
- step = arguments[2] || 1;
- }
- // convert negative steps to positive and reverse min/max
- if (step < 0 && min >= max) {
- step *= -1;
- var tmp = min;
- min = max;
- max = tmp;
- min += ((max-min) % step);
- }
- var a = [];
- for (var i = min; i <= max; i += step) { a.push(i); }
- return a;
- },
-
- // Taken from ui.core.js.
- // Why are you keeping this gem for yourself guys ? :|
- keyCode: {
- BACKSPACE: 8, CAPS_LOCK: 20, COMMA: 188, CONTROL: 17, DELETE: 46, DOWN: 40,
- END: 35, ENTER: 13, ESCAPE: 27, HOME: 36, INSERT: 45, LEFT: 37,
- NUMPAD_ADD: 107, NUMPAD_DECIMAL: 110, NUMPAD_DIVIDE: 111, NUMPAD_ENTER: 108,
- NUMPAD_MULTIPLY: 106, NUMPAD_SUBTRACT: 109, PAGE_DOWN: 34, PAGE_UP: 33,
- PERIOD: 190, RIGHT: 39, SHIFT: 16, SPACE: 32, TAB: 9, UP: 38
- },
-
- // Takes a keyboard event and return true if the keycode match the specified keycode
- keyIs: function(k, e) {
- return parseInt($.keyCode[k.toUpperCase()], 10) == parseInt((typeof(e) == 'number' )? e: e.keyCode, 10);
- },
-
- // Returns the key of an array
- keys: function(arr) {
- var o = [];
- for (k in arr) { o.push(k); }
- return o;
- },
-
- // Redirect to a specified url
- redirect: function(url) {
- window.location.href = url;
- return url;
- },
-
- // Stop event shorthand
- stop: function(e, preventDefault, stopPropagation) {
- if (preventDefault) { e.preventDefault(); }
- if (stopPropagation) { e.stopPropagation(); }
- return preventDefault && false || true;
- },
-
- // Returns the basename of a path
- basename: function(path) {
- var t = path.split('/');
- return t[t.length] === '' && s || t.slice(0, t.length).join('/');
- },
-
- // Returns the filename of a path
- filename: function(path) {
- return path.split('/').pop();
- },
-
- // Returns a formated file size
- filesizeformat: function(bytes, suffixes){
- var b = parseInt(bytes, 10);
- var s = suffixes || ['byte', 'bytes', 'KB', 'MB', 'GB'];
- if (isNaN(b) || b === 0) { return '0 ' + s[0]; }
- if (b == 1) { return '1 ' + s[0]; }
- if (b < 1024) { return b.toFixed(2) + ' ' + s[1]; }
- if (b < 1048576) { return (b / 1024).toFixed(2) + ' ' + s[2]; }
- if (b < 1073741824) { return (b / 1048576).toFixed(2) + ' '+ s[3]; }
- else { return (b / 1073741824).toFixed(2) + ' '+ s[4]; }
- },
-
- fileExtension: function(s) {
- var tokens = s.split('.');
- return tokens[tokens.length-1] || false;
- },
-
- // Returns true if an object is a String
- isString: function(o) {
- return typeof(o) == 'string' && true || false;
- },
-
- // Returns true if an object is a RegExp
- isRegExp: function(o) {
- return o && o.constructor.toString().indexOf('RegExp()') != -1 || false;
- },
-
- isObject: function(o) {
- return (typeof(o) == 'object');
- },
-
- // Convert input to currency (two decimal fixed number)
- toCurrency: function(i) {
- i = parseFloat(i, 10).toFixed(2);
- return (i=='NaN') ? '0.00' : i;
- },
-
- /*--------------------------------------------------------------------
- * javascript method: "pxToEm"
- * by:
- Scott Jehl (scott@filamentgroup.com)
- Maggie Wachs (maggie@filamentgroup.com)
- http://www.filamentgroup.com
- *
- * Copyright (c) 2008 Filament Group
- * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
- *
- * Description: pxToEm converts a pixel value to ems depending on inherited font size.
- * Article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/
- * Demo: http://www.filamentgroup.com/examples/pxToEm/
- *
- * Options:
- scope: string or jQuery selector for font-size scoping
- reverse: Boolean, true reverses the conversion to em-px
- * Dependencies: jQuery library
- * Usage Example: myPixelValue.pxToEm(); or myPixelValue.pxToEm({'scope':'#navigation', reverse: true});
- *
- * Version: 2.1, 18.12.2008
- * Changelog:
- * 08.02.2007 initial Version 1.0
- * 08.01.2008 - fixed font-size calculation for IE
- * 18.12.2008 - removed native object prototyping to stay in jQuery's spirit, jsLinted (Maxime Haineault <haineault@gmail.com>)
- --------------------------------------------------------------------*/
-
- pxToEm: function(i, settings){
- //set defaults
- settings = jQuery.extend({
- scope: 'body',
- reverse: false
- }, settings);
-
- var pxVal = (i === '') ? 0 : parseFloat(i);
- var scopeVal;
- var getWindowWidth = function(){
- var de = document.documentElement;
- return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
- };
-
- /* When a percentage-based font-size is set on the body, IE returns that percent of the window width as the font-size.
- For example, if the body font-size is 62.5% and the window width is 1000px, IE will return 625px as the font-size.
- When this happens, we calculate the correct body font-size (%) and multiply it by 16 (the standard browser font size)
- to get an accurate em value. */
-
- if (settings.scope == 'body' && $.browser.msie && (parseFloat($('body').css('font-size')) / getWindowWidth()).toFixed(1) > 0.0) {
- var calcFontSize = function(){
- return (parseFloat($('body').css('font-size'))/getWindowWidth()).toFixed(3) * 16;
- };
- scopeVal = calcFontSize();
- }
- else { scopeVal = parseFloat(jQuery(settings.scope).css("font-size")); }
-
- var result = (settings.reverse === true) ? (pxVal * scopeVal).toFixed(2) + 'px' : (pxVal / scopeVal).toFixed(2) + 'em';
- return result;
- }
- });
-
- $.extend($.fn, {
- type: function() {
- try { return $(this).get(0).nodeName.toLowerCase(); }
- catch(e) { return false; }
- },
- // Select a text range in a textarea
- selectRange: function(start, end){
- // use only the first one since only one input can be focused
- if ($(this).get(0).createTextRange) {
- var range = $(this).get(0).createTextRange();
- range.collapse(true);
- range.moveEnd('character', end);
- range.moveStart('character', start);
- range.select();
- }
- else if ($(this).get(0).setSelectionRange) {
- $(this).bind('focus', function(e){
- e.preventDefault();
- }).get(0).setSelectionRange(start, end);
- }
- return $(this);
- },
-
- /*--------------------------------------------------------------------
- * JQuery Plugin: "EqualHeights"
- * by: Scott Jehl, Todd Parker, Maggie Costello Wachs (http://www.filamentgroup.com)
- *
- * Copyright (c) 2008 Filament Group
- * Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)
- *
- * Description: Compares the heights or widths of the top-level children of a provided element
- and sets their min-height to the tallest height (or width to widest width). Sets in em units
- by default if pxToEm() method is available.
- * Dependencies: jQuery library, pxToEm method (article:
- http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/)
- * Usage Example: $(element).equalHeights();
- Optional: to set min-height in px, pass a true argument: $(element).equalHeights(true);
- * Version: 2.1, 18.12.2008
- *
- * Note: Changed pxToEm call to call $.pxToEm instead, jsLinted (Maxime Haineault <haineault@gmail.com>)
- --------------------------------------------------------------------*/
-
- equalHeights: function(px){
- $(this).each(function(){
- var currentTallest = 0;
- $(this).children().each(function(i){
- if ($(this).height() > currentTallest) { currentTallest = $(this).height(); }
- });
- if (!px || !$.pxToEm) { currentTallest = $.pxToEm(currentTallest); } //use ems unless px is specified
- // for ie6, set height since min-height isn't supported
- if ($.browser.msie && $.browser.version == 6.0) { $(this).children().css({'height': currentTallest}); }
- $(this).children().css({'min-height': currentTallest});
- });
- return this;
- },
-
- // Copyright (c) 2009 James Padolsey
- // http://james.padolsey.com/javascript/jquery-delay-plugin/
- delay: function(time, callback){
- jQuery.fx.step.delay = function(){};
- return this.animate({delay:1}, time, callback);
- }
- });
-})(jQuery);
-
-/*
- jQuery strings - 0.4
- http://code.google.com/p/jquery-utils/
-
- (c) Maxime Haineault <haineault@gmail.com>
- http://haineault.com
-
- MIT License (http://www.opensource.org/licenses/mit-license.php)
-
- Implementation of Python3K advanced string formatting
- http://www.python.org/dev/peps/pep-3101/
-
- Documentation: http://code.google.com/p/jquery-utils/wiki/StringFormat
-
-*/
-(function($){
- var strings = {
- strConversion: {
- // tries to translate any objects type into string gracefully
- __repr: function(i){
- switch(this.__getType(i)) {
- case 'array':case 'date':case 'number':
- return i.toString();
- case 'object': // Thanks to Richard Paul Lewis for the fix
- var o = [];
- var l = i.length;
- for(var x=0;x<l;x++) {
- o.push(x+': '+this.__repr(i[x]));
- }
- return o.join(', ');
- case 'string':
- return i;
- default:
- return i;
- }
- },
- // like typeof but less vague
- __getType: function(i) {
- if (!i || !i.constructor) { return typeof(i); }
- var match = i.constructor.toString().match(/Array|Number|String|Object|Date/);
- return match && match[0].toLowerCase() || typeof(i);
- },
- // Jonas Raoni Soares Silva (http://jsfromhell.com/string/pad)
- __pad: function(str, l, s, t){
- var p = s || ' ';
- var o = str;
- if (l - str.length > 0) {
- o = new Array(Math.ceil(l / p.length)).join(p).substr(0, t = !t ? l : t == 1 ? 0 : Math.ceil(l / 2)) + str + p.substr(0, l - t);
- }
- return o;
- },
- __getInput: function(arg, args) {
- var key = arg.getKey();
- switch(this.__getType(args)){
- case 'object': // Thanks to Jonathan Works for the patch
- var keys = key.split('.');
- var obj = args;
- for(var subkey = 0; subkey < keys.length; subkey++){
- obj = obj[keys[subkey]];
- }
- if (typeof(obj) != 'undefined') {
- if (strings.strConversion.__getType(obj) == 'array') {
- return arg.getFormat().match(/\.\*/) && obj[1] || obj;
- }
- return obj;
- }
- else {
- // TODO: try by numerical index
- }
- break;
- case 'array':
- key = parseInt(key, 10);
- if (arg.getFormat().match(/\.\*/) && typeof args[key+1] != 'undefined') { return args[key+1]; }
- else if (typeof args[key] != 'undefined') { return args[key]; }
- else { return key; }
- break;
- }
- return '{'+key+'}';
- },
- __formatToken: function(token, args) {
- var arg = new Argument(token, args);
- return strings.strConversion[arg.getFormat().slice(-1)](this.__getInput(arg, args), arg);
- },
-
- // Signed integer decimal.
- d: function(input, arg){
- var o = parseInt(input, 10); // enforce base 10
- var p = arg.getPaddingLength();
- if (p) { return this.__pad(o.toString(), p, arg.getPaddingString(), 0); }
- else { return o; }
- },
- // Signed integer decimal.
- i: function(input, args){
- return this.d(input, args);
- },
- // Unsigned octal
- o: function(input, arg){
- var o = input.toString(8);
- if (arg.isAlternate()) { o = this.__pad(o, o.length+1, '0', 0); }
- return this.__pad(o, arg.getPaddingLength(), arg.getPaddingString(), 0);
- },
- // Unsigned decimal
- u: function(input, args) {
- return Math.abs(this.d(input, args));
- },
- // Unsigned hexadecimal (lowercase)
- x: function(input, arg){
- var o = parseInt(input, 10).toString(16);
- o = this.__pad(o, arg.getPaddingLength(), arg.getPaddingString(),0);
- return arg.isAlternate() ? '0x'+o : o;
- },
- // Unsigned hexadecimal (uppercase)
- X: function(input, arg){
- return this.x(input, arg).toUpperCase();
- },
- // Floating point exponential format (lowercase)
- e: function(input, arg){
- return parseFloat(input, 10).toExponential(arg.getPrecision());
- },
- // Floating point exponential format (uppercase)
- E: function(input, arg){
- return this.e(input, arg).toUpperCase();
- },
- // Floating point decimal format
- f: function(input, arg){
- return this.__pad(parseFloat(input, 10).toFixed(arg.getPrecision()), arg.getPaddingLength(), arg.getPaddingString(),0);
- },
- // Floating point decimal format (alias)
- F: function(input, args){
- return this.f(input, args);
- },
- // Floating point format. Uses exponential format if exponent is greater than -4 or less than precision, decimal format otherwise
- g: function(input, arg){
- var o = parseFloat(input, 10);
- return (o.toString().length > 6) ? Math.round(o.toExponential(arg.getPrecision())): o;
- },
- // Floating point format. Uses exponential format if exponent is greater than -4 or less than precision, decimal format otherwise
- G: function(input, args){
- return this.g(input, args);
- },
- // Single character (accepts integer or single character string).
- c: function(input, args) {
- var match = input.match(/\w|\d/);
- return match && match[0] || '';
- },
- // String (converts any JavaScript object to anotated format)
- r: function(input, args) {
- return this.__repr(input);
- },
- // String (converts any JavaScript object using object.toString())
- s: function(input, args) {
- return input.toString && input.toString() || ''+input;
- }
- },
-
- format: function(str, args) {
- var end = 0;
- var start = 0;
- var match = false;
- var buffer = [];
- var token = '';
- var tmp = (str||'').split('');
- for(start=0; start < tmp.length; start++) {
- if (tmp[start] == '{' && tmp[start+1] !='{') {
- end = str.indexOf('}', start);
- token = tmp.slice(start+1, end).join('');
- if (tmp[start-1] != '{' && tmp[end+1] != '}') {
- var tokenArgs = (typeof arguments[1] != 'object')? arguments2Array(arguments, 2): args || [];
- buffer.push(strings.strConversion.__formatToken(token, tokenArgs));
- }
- else {
- buffer.push(token);
- }
- }
- else if (start > end || buffer.length < 1) { buffer.push(tmp[start]); }
- }
- return (buffer.length > 1)? buffer.join(''): buffer[0];
- },
-
- calc: function(str, args) {
- return eval(format(str, args));
- },
-
- repeat: function(s, n) {
- return new Array(n+1).join(s);
- },
-
- UTF8encode: function(s) {
- return unescape(encodeURIComponent(s));
- },
-
- UTF8decode: function(s) {
- return decodeURIComponent(escape(s));
- },
-
- tpl: function() {
- var out = '';
- var render = true;
- // Set
- // $.tpl('ui.test', ['<span>', helloWorld ,'</span>']);
- if (arguments.length == 2 && $.isArray(arguments[1])) {
- this[arguments[0]] = arguments[1].join('');
- return $(this[arguments[0]]);
- }
- // $.tpl('ui.test', '<span>hello world</span>');
- if (arguments.length == 2 && $.isString(arguments[1])) {
- this[arguments[0]] = arguments[1];
- return $(this[arguments[0]]);
- }
- // Call
- // $.tpl('ui.test');
- if (arguments.length == 1) {
- return $(this[arguments[0]]);
- }
- // $.tpl('ui.test', false);
- if (arguments.length == 2 && arguments[1] == false) {
- return this[arguments[0]];
- }
- // $.tpl('ui.test', {value:blah});
- if (arguments.length == 2 && $.isObject(arguments[1])) {
- return $($.format(this[arguments[0]], arguments[1]));
- }
- // $.tpl('ui.test', {value:blah}, false);
- if (arguments.length == 3 && $.isObject(arguments[1])) {
- return (arguments[2] == true)
- ? $.format(this[arguments[0]], arguments[1])
- : $($.format(this[arguments[0]], arguments[1]));
- }
- }
- };
-
- var Argument = function(arg, args) {
- this.__arg = arg;
- this.__args = args;
- this.__max_precision = parseFloat('1.'+ (new Array(32)).join('1'), 10).toString().length-3;
- this.__def_precision = 6;
- this.getString = function(){
- return this.__arg;
- };
- this.getKey = function(){
- return this.__arg.split(':')[0];
- };
- this.getFormat = function(){
- var match = this.getString().split(':');
- return (match && match[1])? match[1]: 's';
- };
- this.getPrecision = function(){
- var match = this.getFormat().match(/\.(\d+|\*)/g);
- if (!match) { return this.__def_precision; }
- else {
- match = match[0].slice(1);
- if (match != '*') { return parseInt(match, 10); }
- else if(strings.strConversion.__getType(this.__args) == 'array') {
- return this.__args[1] && this.__args[0] || this.__def_precision;
- }
- else if(strings.strConversion.__getType(this.__args) == 'object') {
- return this.__args[this.getKey()] && this.__args[this.getKey()][0] || this.__def_precision;
- }
- else { return this.__def_precision; }
- }
- };
- this.getPaddingLength = function(){
- var match = false;
- if (this.isAlternate()) {
- match = this.getString().match(/0?#0?(\d+)/);
- if (match && match[1]) { return parseInt(match[1], 10); }
- }
- match = this.getString().match(/(0|\.)(\d+|\*)/g);
- return match && parseInt(match[0].slice(1), 10) || 0;
- };
- this.getPaddingString = function(){
- var o = '';
- if (this.isAlternate()) { o = ' '; }
- // 0 take precedence on alternate format
- if (this.getFormat().match(/#0|0#|^0|\.\d+/)) { o = '0'; }
- return o;
- };
- this.getFlags = function(){
- var match = this.getString().matc(/^(0|\#|\-|\+|\s)+/);
- return match && match[0].split('') || [];
- };
- this.isAlternate = function() {
- return !!this.getFormat().match(/^0?#/);
- };
- };
-
- var arguments2Array = function(args, shift) {
- var o = [];
- for (l=args.length, x=(shift || 0)-1; x<l;x++) { o.push(args[x]); }
- return o;
- };
- $.extend(strings);
-})(jQuery);
-
-/*
- jQuery ui.timepickr - @VERSION
- http://code.google.com/p/jquery-utils/
-
- (c) Maxime Haineault <haineault@gmail.com>
- http://haineault.com
-
- MIT License (http://www.opensource.org/licenses/mit-license.php
-
- Note: if you want the original experimental plugin checkout the rev 224
-
- Dependencies
- ------------
- - jquery.utils.js
- - jquery.strings.js
- - jquery.ui.js
-
-*/
-
-(function($) {
-
-$.tpl('timepickr.menu', '<div class="ui-helper-reset ui-timepickr ui-widget" />');
-$.tpl('timepickr.row', '<ol class="ui-timepickr-row ui-helper-clearfix" />');
-$.tpl('timepickr.button', '<li class="{className:s}"><span class="ui-state-default">{label:s}</span></li>');
-
-$.widget('ui.timepickr', {
- plugins: {},
- _create: function() {
- this._dom = {
- menu: $.tpl('timepickr.menu'),
- row: $.tpl('timepickr.menu')
- };
- this._trigger('initialize');
- this._trigger('initialized');
- },
-
- _trigger: function(type, e, ui) {
- var ui = ui || this;
- $.ui.plugin.call(this, type, [e, ui]);
- return $.Widget.prototype._trigger.call(this, type, e, ui);
- },
-
- _createButton: function(i, format, className) {
- var o = format && $.format(format, i) || i;
- var cn = className && 'ui-timepickr-button '+ className || 'ui-timepickr-button';
- return $.tpl('timepickr.button', {className: cn, label: o}).data('id', i)
- .bind('mouseover', function(){
- $(this).siblings().find('span')
- .removeClass('ui-state-hover').end().end()
- .find('span').addClass('ui-state-hover');
- });
-
- },
-
- _addRow: function(range, format, className, insertAfter) {
- var ui = this;
- var btn = false;
- var row = $.tpl('timepickr.row').bind('mouseover', function(){
- $(this).next().show();
- });
- $.each(range, function(idx, val){
- ui._createButton(val, format || false).appendTo(row);
- });
- if (className) {
- $(row).addClass(className);
- }
- if (this.options.corners) {
- row.find('span').addClass('ui-corner-'+ this.options.corners);
- }
- if (insertAfter) {
- row.insertAfter(insertAfter);
- }
- else {
- ui._dom.menu.append(row);
- }
- return row;
- },
-
- _setVal: function(val) {
- val = val || this._getVal();
- if (!(val.h==='' && val.m==='')) {
- this.element.data('timepickr.initialValue', val);
- this.element.val(this._formatVal(val));
- }
- if(this._dom.menu.is(':hidden')) {
- this.element.trigger('change');
- }
- },
-
- _getVal: function() {
- var ols = this._dom.menu.find('ol');
- function get(unit) {
- var u = ols.filter('.'+unit).find('.ui-state-hover:first').text();
- return u || ols.filter('.'+unit+'li:first span').text();
- }
- return {
- h: get('hours'),
- m: get('minutes'),
- s: get('seconds'),
- a: get('prefix'),
- z: get('suffix'),
- f: this.options['format'+ this.c],
- c: this.c
- };
- },
-
- _formatVal: function(ival) {
- var val = ival || this._getVal();
- val.c = this.options.convention;
- val.f = val.c === 12 && this.options.format12 || this.options.format24;
- return (new Time(val)).getTime();
- },
-
- blur: function() {
- return this.element.blur();
- },
-
- focus: function() {
- return this.element.focus();
- },
- show: function() {
- this._trigger('show');
- this.element.trigger(this.options.trigger);
- },
- hide: function() {
- this._trigger('hide');
- this._dom.menu.hide();
- }
-
-});
-
-// These properties are shared accross every instances of timepickr
-$.extend($.ui.timepickr.prototype, {
- version: '@VERSION',
- //eventPrefix: '',
- //getter: '',
- options: {
- convention: 24, // 24, 12
- trigger: 'mouseover',
- format12: '{h:02.d}:{m:02.d} {z:s}',
- format24: '{h:02.d}:{m:02.d}',
- hours: true,
- prefix: ['am', 'pm'],
- suffix: ['am', 'pm'],
- prefixVal: false,
- suffixVal: true,
- rangeHour12: $.range(1, 13),
- rangeHour24: [$.range(0, 12), $.range(12, 24)],
- rangeMin: $.range(0, 60, 15),
- rangeSec: $.range(0, 60, 15),
- corners: 'all',
- // plugins
- core: true,
- minutes: true,
- seconds: false,
- val: false,
- updateLive: true,
- resetOnBlur: true,
- keyboardnav: true,
- handle: false,
- handleEvent: 'click'
- }
-});
-
-$.ui.plugin.add('timepickr', 'core', {
- initialized: function(e, ui) {
- var menu = ui._dom.menu;
- var pos = ui.element.position();
-
- menu.insertAfter(ui.element).css('left', pos.left);
-
- if (!$.boxModel) { // IE alignement fix
- menu.css('margin-top', ui.element.height() + 8);
- }
-
- ui.element
- .bind(ui.options.trigger, function() {
- ui._dom.menu.show();
- ui._dom.menu.find('ol:first').show();
- ui._trigger('focus');
- if (ui.options.trigger != 'focus') {
- ui.element.focus();
- }
- ui._trigger('focus');
- })
- .bind('blur', function() {
- ui.hide();
- ui._trigger('blur');
- });
-
- menu.find('li').bind('mouseover.timepickr', function() {
- ui._trigger('refresh');
- });
- },
- refresh: function(e, ui) {
- // Realign each menu layers
- ui._dom.menu.find('ol').each(function(){
- var p = $(this).prev('ol');
- try { // .. to not fuckup IE
- $(this).css('left', p.position().left + p.find('.ui-state-hover').position().left);
- } catch(e) {};
- });
- }
-});
-
-$.ui.plugin.add('timepickr', 'hours', {
- initialize: function(e, ui) {
- if (ui.options.convention === 24) {
- // prefix is required in 24h mode
- ui._dom.prefix = ui._addRow(ui.options.prefix, false, 'prefix');
-
- // split-range
- if ($.isArray(ui.options.rangeHour24[0])) {
- var range = [];
- $.merge(range, ui.options.rangeHour24[0]);
- $.merge(range, ui.options.rangeHour24[1]);
- ui._dom.hours = ui._addRow(range, '{0:0.2d}', 'hours');
- ui._dom.hours.find('li').slice(ui.options.rangeHour24[0].length, -1).hide();
- var lis = ui._dom.hours.find('li');
-
- var show = [
- function() {
- lis.slice(ui.options.rangeHour24[0].length).hide().end()
- .slice(0, ui.options.rangeHour24[0].length).show()
- .filter(':visible:first').trigger('mouseover');
-
- },
- function() {
- lis.slice(0, ui.options.rangeHour24[0].length).hide().end()
- .slice(ui.options.rangeHour24[0].length).show()
- .filter(':visible:first').trigger('mouseover');
- }
- ];
-
- ui._dom.prefix.find('li').bind('mouseover.timepickr', function(){
- var index = ui._dom.menu.find('.prefix li').index(this);
- show[index].call();
- });
- }
- else {
- ui._dom.hours = ui._addRow(ui.options.rangeHour24, '{0:0.2d}', 'hours');
- ui._dom.hours.find('li').slice(12, -1).hide();
- }
- }
- else {
- ui._dom.hours = ui._addRow(ui.options.rangeHour12, '{0:0.2d}', 'hours');
- // suffix is required in 12h mode
- ui._dom.suffix = ui._addRow(ui.options.suffix, false, 'suffix');
- }
- }});
-
-$.ui.plugin.add('timepickr', 'minutes', {
- initialize: function(e, ui) {
- var p = ui._dom.hours && ui._dom.hours || false;
- ui._dom.minutes = ui._addRow(ui.options.rangeMin, '{0:0.2d}', 'minutes', p);
- }
-});
-
-$.ui.plugin.add('timepickr', 'seconds', {
- initialize: function(e, ui) {
- var p = ui._dom.minutes && ui._dom.minutes || false;
- ui._dom.seconds = ui._addRow(ui.options.rangeSec, '{0:0.2d}', 'seconds', p);
- }
-});
-
-$.ui.plugin.add('timepickr', 'val', {
- initialized: function(e, ui) {
- ui._setVal(ui.options.val);
- }
-});
-
-$.ui.plugin.add('timepickr', 'updateLive', {
- refresh: function(e, ui) {
- ui._setVal();
- }
-});
-
-$.ui.plugin.add('timepickr', 'resetOnBlur', {
- initialized: function(e, ui) {
- ui.element.data('timepickr.initialValue', ui._getVal());
- ui._dom.menu.find('li > span').bind('mousedown.timepickr', function(){
- ui.element.data('timepickr.initialValue', ui._getVal());
- });
- },
- blur: function(e, ui) {
- ui._setVal(ui.element.data('timepickr.initialValue'));
- }
-});
-
-$.ui.plugin.add('timepickr', 'handle', {
- initialized: function(e, ui) {
- $(ui.options.handle).bind(ui.options.handleEvent + '.timepickr', function(){
- ui.show();
- });
- }
-});
-
-$.ui.plugin.add('timepickr', 'keyboardnav', {
- initialized: function(e, ui) {
- ui.element
- .bind('keydown', function(e) {
- if ($.keyIs('enter', e)) {
- ui._setVal();
- ui.blur();
- }
- else if ($.keyIs('escape', e)) {
- ui.blur();
- }
- });
- }
-});
-
-var Time = function() { // arguments: h, m, s, c, z, f || time string
- if (!(this instanceof arguments.callee)) {
- throw Error("Constructor called as a function");
- }
- // arguments as literal object
- if (arguments.length == 1 && $.isObject(arguments[0])) {
- this.h = arguments[0].h || 0;
- this.m = arguments[0].m || 0;
- this.s = arguments[0].s || 0;
- this.c = arguments[0].c && ($.inArray(arguments[0].c, [12, 24]) >= 0) && arguments[0].c || 24;
- this.f = arguments[0].f || ((this.c == 12) && '{h:02.d}:{m:02.d} {z:02.d}' || '{h:02.d}:{m:02.d}');
- this.z = arguments[0].z || 'am';
- }
- // arguments as string
- else if (arguments.length < 4 && $.isString(arguments[1])) {
- this.c = arguments[2] && ($.inArray(arguments[0], [12, 24]) >= 0) && arguments[0] || 24;
- this.f = arguments[3] || ((this.c == 12) && '{h:02.d}:{m:02.d} {z:02.d}' || '{h:02.d}:{m:02.d}');
- this.z = arguments[4] || 'am';
-
- this.h = arguments[1] || 0; // parse
- this.m = arguments[1] || 0; // parse
- this.s = arguments[1] || 0; // parse
- }
- // no arguments (now)
- else if (arguments.length === 0) {
- // now
- }
- // standards arguments
- else {
- this.h = arguments[0] || 0;
- this.m = arguments[1] || 0;
- this.s = arguments[2] || 0;
- this.c = arguments[3] && ($.inArray(arguments[3], [12, 24]) >= 0) && arguments[3] || 24;
- this.f = this.f || ((this.c == 12) && '{h:02.d}:{m:02.d} {z:02.d}' || '{h:02.d}:{m:02.d}');
- this.z = 'am';
- }
- return this;
-};
-
-Time.prototype.get = function(p, f, u) { return u && this.h || $.format(f, this.h); };
-Time.prototype.getHours = function(unformated) { return this.get('h', '{0:02.d}', unformated); };
-Time.prototype.getMinutes = function(unformated) { return this.get('m', '{0:02.d}', unformated); };
-Time.prototype.getSeconds = function(unformated) { return this.get('s', '{0:02.d}', unformated); };
-Time.prototype.setFormat = function(format) { return this.f = format; };
-Time.prototype.getObject = function() { return { h: this.h, m: this.m, s: this.s, c: this.c, f: this.f, z: this.z }; };
-Time.prototype.getTime = function() { return $.format(this.f, {h: this.h, m: this.m, suffix: this.z}); }; // Thanks to Jackson for the fix.
-Time.prototype.parse = function(str) {
- // 12h formats
- if (this.c === 12) {
- // Supported formats: (can't find any *official* standards for 12h..)
- // - [hh]:[mm]:[ss] [zz] | [hh]:[mm] [zz] | [hh] [zz]
- // - [hh]:[mm]:[ss] [z.z.] | [hh]:[mm] [z.z.] | [hh] [z.z.]
- this.tokens = str.split(/\s|:/);
- this.h = this.tokens[0] || 0;
- this.m = this.tokens[1] || 0;
- this.s = this.tokens[2] || 0;
- this.z = this.tokens[3] || '';
- return this.getObject();
- }
- // 24h formats
- else {
- // Supported formats:
- // - ISO 8601: [hh][mm][ss] | [hh][mm] | [hh]
- // - ISO 8601 extended: [hh]:[mm]:[ss] | [hh]:[mm] | [hh]
- this.tokens = /:/.test(str) && str.split(/:/) || str.match(/[0-9]{2}/g);
- this.h = this.tokens[0] || 0;
- this.m = this.tokens[1] || 0;
- this.s = this.tokens[2] || 0;
- this.z = this.tokens[3] || '';
- return this.getObject();
- }
-};
-
-})(jQuery);
diff --git a/rt/share/html/NoAuth/js/util.js b/rt/share/html/NoAuth/js/util.js
index 5bfce411d..fe5c0a3ff 100644
--- a/rt/share/html/NoAuth/js/util.js
+++ b/rt/share/html/NoAuth/js/util.js
@@ -222,35 +222,47 @@ function doOnLoad( js ) {
}
jQuery(function() {
- jQuery(".ui-datepicker:not(.withtime)").datepicker( {
- dateFormat: 'yy-mm-dd',
- constrainInput: false
- } );
-
- jQuery(".ui-datepicker.withtime").datepicker( {
+ var opts = {
dateFormat: 'yy-mm-dd',
constrainInput: false,
- onSelect: function( dateText, inst ) {
- // trigger timepicker to get time
- var button = document.createElement('input');
- button.setAttribute('type', 'button');
- jQuery(button).width('5em');
- jQuery(button).insertAfter(this);
- jQuery(button).timepickr({val: '00:00'});
- var date_input = this;
-
- jQuery(button).blur( function() {
- var time = jQuery(button).val();
- if ( ! time.match(/\d\d:\d\d/) ) {
- time = '00:00';
- }
- jQuery(date_input).val( dateText + ' ' + time + ':00' );
- jQuery(button).remove();
- } );
-
- jQuery(button).focus();
- }
- } );
+ showButtonPanel: true,
+ changeMonth: true,
+ changeYear: true,
+ showOtherMonths: true,
+ selectOtherMonths: true
+ };
+ jQuery(".ui-datepicker:not(.withtime)").datepicker(opts);
+ jQuery(".ui-datepicker.withtime").datetimepicker( jQuery.extend({}, opts, {
+ stepHour: 1,
+ // We fake this by snapping below for the minute slider
+ //stepMinute: 5,
+ hourGrid: 6,
+ minuteGrid: 15,
+ showSecond: false,
+ timeFormat: 'hh:mm:ss'
+ }) ).each(function(index, el) {
+ var tp = jQuery.datepicker._get( jQuery.datepicker._getInst(el), 'timepicker');
+ if (!tp) return;
+
+ // Hook after _injectTimePicker so we can modify the minute_slider
+ // right after it's first created
+ tp._base_injectTimePicker = tp._injectTimePicker;
+ tp._injectTimePicker = function() {
+ this._base_injectTimePicker.apply(this, arguments);
+
+ // Now that we have minute_slider, modify it to be stepped for mouse movements
+ var slider = jQuery.data(this.minute_slider[0], "slider");
+ slider._base_normValueFromMouse = slider._normValueFromMouse;
+ slider._normValueFromMouse = function() {
+ var value = this._base_normValueFromMouse.apply(this, arguments);
+ var old_step = this.options.step;
+ this.options.step = 5;
+ var aligned = this._trimAlignValue( value );
+ this.options.step = old_step;
+ return aligned;
+ };
+ };
+ });
});
function textToHTML(value) {
diff --git a/rt/share/html/Prefs/Other.html b/rt/share/html/Prefs/Other.html
index 9f7e04a9c..b5d3edd95 100644
--- a/rt/share/html/Prefs/Other.html
+++ b/rt/share/html/Prefs/Other.html
@@ -53,6 +53,7 @@
% foreach my $section( RT->Config->Sections ) {
<&|/Widgets/TitleBox, title => loc( $section ) &>
% foreach my $option( RT->Config->Options( Section => $section ) ) {
+% next if $option eq 'EmailFrequency' && !RT->Config->Get('RecordOutgoingEmail');
% my $meta = RT->Config->Meta( $option );
<& $meta->{'Widget'},
Default => 1,
diff --git a/rt/share/html/REST/1.0/Forms/ticket/default b/rt/share/html/REST/1.0/Forms/ticket/default
index 9a2212b55..016a50c73 100755
--- a/rt/share/html/REST/1.0/Forms/ticket/default
+++ b/rt/share/html/REST/1.0/Forms/ticket/default
@@ -167,6 +167,17 @@ else {
elsif (lc $k eq 'text') {
$text = delete $data{$k};
}
+ elsif ( lc $k ne 'id' ) {
+ $e = 1;
+ push @$o, $k;
+ push(@comments, "# $k: Unknown field");
+ }
+ }
+
+ if ( $e ) {
+ unshift @comments, "# Could not create ticket.";
+ $k = \%data;
+ goto DONE;
}
# people fields allow multiple values
@@ -292,8 +303,10 @@ else {
elsif (exists $simple{$key}) {
$key = $simple{$key};
$set = "Set$key";
+ my $current = $ticket->$key;
+ $current = '' unless defined $current;
- next if (($val eq ($ticket->$key||''))|| ($ticket->$key =~ /^\d+$/ && $val =~ /^\d+$/ && $val == $ticket->$key));
+ next if ($val eq $current) or ($current =~ /^\d+$/ && $val =~ /^\d+$/ && $val == $current);
($n, $s) = $ticket->$set("$val");
}
elsif (exists $dates{$key}) {
@@ -331,13 +344,6 @@ else {
}
}
foreach $p (keys %new) {
- # XXX: This is a stupid test.
- unless ($p =~ /^[\w.+-]+\@([\w.-]+\.)*\w+.?$/) {
- $s = 0;
- $n = "$p is not a valid email address.";
- push @msgs, [ $s, $n ];
- next;
- }
unless ($ticket->IsWatcher(Type => $type, Email => $p)) {
($s, $n) = $ticket->AddWatcher(Type => $type,
Email => $p);
diff --git a/rt/share/html/Search/Chart.html b/rt/share/html/Search/Chart.html
index 070ce7cf7..571c3d3a0 100644
--- a/rt/share/html/Search/Chart.html
+++ b/rt/share/html/Search/Chart.html
@@ -98,14 +98,14 @@ my %query;
for(@session_fields) {
$query{$_} = $current->{$_} unless defined $query{$_};
- $query{$_} = $m->request_args->{$_} unless defined $query{$_};
+ $query{$_} = $DECODED_ARGS->{$_} unless defined $query{$_};
}
- if ($m->request_args->{'SavedSearchLoadSubmit'}) {
- $query{'SavedChartSearchId'} = $m->request_args->{'SavedSearchLoad'};
+ if ($DECODED_ARGS->{'SavedSearchLoadSubmit'}) {
+ $query{'SavedChartSearchId'} = $DECODED_ARGS->{'SavedSearchLoad'};
}
- if ($m->request_args->{'SavedSearchSave'}) {
+ if ($DECODED_ARGS->{'SavedSearchSave'}) {
$query{'SavedChartSearchId'} = $saved_search->{'SearchId'};
}
diff --git a/rt/share/html/Search/Elements/SelectPersonType b/rt/share/html/Search/Elements/SelectPersonType
index d07e49c22..bc2911196 100644
--- a/rt/share/html/Search/Elements/SelectPersonType
+++ b/rt/share/html/Search/Elements/SelectPersonType
@@ -62,7 +62,7 @@
<%INIT>
my @types;
-if ($Scope =~ 'queue') {
+if ($Scope =~ /queue/) {
@types = qw(Cc AdminCc);
}
elsif ($Suffix eq 'Group') {
diff --git a/rt/share/html/Search/Results.html b/rt/share/html/Search/Results.html
index 171b38d92..4fee86506 100755
--- a/rt/share/html/Search/Results.html
+++ b/rt/share/html/Search/Results.html
@@ -151,6 +151,7 @@ if ($ARGS{'TicketsRefreshInterval'}) {
my $refresh = $session{'tickets_refresh_interval'}
|| RT->Config->Get('SearchResultsRefreshInterval', $session{'CurrentUser'} );
+# Check $m->request_args, not $DECODED_ARGS, to avoid creating a new CSRF token on each refresh
if (RT->Config->Get('RestrictReferrer') and $refresh and not $m->request_args->{CSRF_Token}) {
my $token = RT::Interface::Web::StoreRequestToken( $session{'CurrentSearchHash'} );
$m->notes->{RefreshURL} = RT->Config->Get('WebURL')
diff --git a/rt/share/html/Search/Results.xls b/rt/share/html/Search/Results.xls
index 52a05daed..8b94e22ba 100644
--- a/rt/share/html/Search/Results.xls
+++ b/rt/share/html/Search/Results.xls
@@ -54,6 +54,7 @@ $Format => undef
<%INIT>
use Spreadsheet::WriteExcel;
+use OLE::Storage_Lite;
use List::Util qw( max );
use Date::Format qw( time2str );
diff --git a/rt/share/html/Ticket/Attachment/dhandler b/rt/share/html/Ticket/Attachment/dhandler
index 75efeffbc..eb291e4f5 100755
--- a/rt/share/html/Ticket/Attachment/dhandler
+++ b/rt/share/html/Ticket/Attachment/dhandler
@@ -48,7 +48,7 @@
<%perl>
my ($ticket, $trans,$attach, $filename);
my $arg = $m->dhandler_arg; # get rest of path
- if ($arg =~ '^(\d+)/(\d+)') {
+ if ($arg =~ m{^(\d+)/(\d+)}) {
$trans = $1;
$attach = $2;
}
@@ -79,7 +79,12 @@
my $enc = $AttachmentObj->OriginalEncoding || 'utf-8';
my $iana = Encode::find_encoding( $enc );
$iana = $iana? $iana->mime_name : $enc;
- $content_type .= ";charset=$iana";
+
+ require MIME::Types;
+ my $mimetype = MIME::Types->new->type($content_type);
+ unless ( $mimetype && $mimetype->isBinary ) {
+ $content_type .= ";charset=$iana";
+ }
$r->subprocess_env('no-gzip' => 1); # disable mod_deflate
$r->content_type( $content_type );
diff --git a/rt/share/html/Ticket/Elements/AddCustomers b/rt/share/html/Ticket/Elements/AddCustomers
index 3c2c82add..13fb2f010 100644
--- a/rt/share/html/Ticket/Elements/AddCustomers
+++ b/rt/share/html/Ticket/Elements/AddCustomers
@@ -58,7 +58,7 @@ my @Customers = ();
if ( $CustomerString ) {
@Customers = &RT::URI::freeside::smart_search(
'search' => $CustomerString,
- 'no_fuzzy_on_exact' => 1, #pref?
+ 'no_fuzzy_on_exact' => ! $FS::CurrentUser::CurrentUser->option('enable_fuzzy_on_exact'),
);
}
diff --git a/rt/share/html/Ticket/Elements/ShowMembers b/rt/share/html/Ticket/Elements/ShowMembers
index c17c6e7b8..1ffbda2a1 100755
--- a/rt/share/html/Ticket/Elements/ShowMembers
+++ b/rt/share/html/Ticket/Elements/ShowMembers
@@ -48,8 +48,9 @@
<ul>
% while (my $link = $members->Next) {
<li><& /Elements/ShowLink, URI => $link->BaseURI &><br />
+% next if $link->BaseObj and $checked->{$link->BaseObj->id};
% if ($depth < 8) {
-<& /Ticket/Elements/ShowMembers, Ticket => $link->BaseObj, depth => ($depth+1) &>
+<& /Ticket/Elements/ShowMembers, Ticket => $link->BaseObj, depth => ($depth+1), checked => $checked &>
% }
</li>
% }
@@ -61,9 +62,13 @@ return unless $Ticket;
my $members = $Ticket->Members;
return unless $members->Count;
+return if $checked->{$Ticket->id};
+
+$checked->{$Ticket->id} = 1;
</%INIT>
<%ARGS>
$Ticket => undef
$depth => 1
+$checked => {}
</%ARGS>
diff --git a/rt/share/html/Ticket/Elements/ShowTransactionAttachments b/rt/share/html/Ticket/Elements/ShowTransactionAttachments
index 877201f55..95a23411b 100644
--- a/rt/share/html/Ticket/Elements/ShowTransactionAttachments
+++ b/rt/share/html/Ticket/Elements/ShowTransactionAttachments
@@ -144,6 +144,8 @@ my $render_attachment = sub {
my $message = shift;
my $name = defined $message->Filename && length $message->Filename ? $message->Filename : '';
+ my $content_type = lc $message->ContentType;
+
# if it has a content-disposition: attachment, don't show inline
my $disposition = $message->GetHeader('Content-Disposition');
@@ -154,7 +156,7 @@ my $render_attachment = sub {
}
# If it's text
- if ( $message->ContentType =~ m{^(text|message)}i ) {
+ if ( $content_type =~ m{^(text|message)/} ) {
my $max_size = RT->Config->Get( 'MaxInlineBody', $session{'CurrentUser'} );
if ( $disposition ne 'inline' ) {
$m->out('<p>'. loc( 'Message body is not shown because sender requested not to inline it.' ) .'</p>');
@@ -175,16 +177,16 @@ my $render_attachment = sub {
!$ParentObj
# or its parent isn't a multipart alternative
- || ( $ParentObj->ContentType !~ m{^multipart/alternative$}i )
+ || ( $ParentObj->ContentType !~ m{^multipart/(?:alternative|related)$}i )
# or it's of our prefered alterative type
|| (
(
RT->Config->Get('PreferRichText')
- && ( $message->ContentType =~ m{^text/(?:html|enriched)$} )
+ && ( $content_type =~ m{^text/(?:html|enriched)$} )
)
|| ( !RT->Config->Get('PreferRichText')
- && ( $message->ContentType !~ m{^text/(?:html|enriched)$} )
+ && ( $content_type !~ m{^text/(?:html|enriched)$} )
)
)
) {
@@ -198,7 +200,6 @@ my $render_attachment = sub {
$content = $message->Content;
}
- my $content_type = lc $message->ContentType;
$RT::Logger->debug(
"Rendering attachment #". $message->id
." of '$content_type' type"
@@ -231,9 +232,8 @@ my $render_attachment = sub {
$m->out( $content );
}
- # if it's a text/plain show the body
- elsif ( $message->ContentType =~ m{^(text|message)}i ) {
-
+ # It's a text type we don't have special handling for
+ else {
unless ( length $name ) {
eval { require Text::Quoted; $content = Text::Quoted::extract($content); };
if ($@) { $RT::Logger->warning( "Text::Quoted failed: $@" ) }
@@ -250,7 +250,7 @@ my $render_attachment = sub {
}
# if it's an image, show it as an image
- elsif ( RT->Config->Get('ShowTransactionImages') and $message->ContentType =~ /^image\//i ) {
+ elsif ( RT->Config->Get('ShowTransactionImages') and $content_type =~ m{^image/} ) {
if ( $disposition ne 'inline' ) {
$m->out('<p>'. loc( 'Message body is not shown because sender requested not to inline it.' ) .'</p>');
return;
diff --git a/rt/share/html/m/_elements/raw_style b/rt/share/html/m/_elements/raw_style
index 8c1997743..a34982958 100644
--- a/rt/share/html/m/_elements/raw_style
+++ b/rt/share/html/m/_elements/raw_style
@@ -33,6 +33,7 @@ div.buttons {
background-color: #ccc;
-moz-border-radius: 0.25em;
-webkit-border-radius: 0.25em;
+ border-radius: 0.25em;
-webkit-box-shadow: #333 0px 0px 5px;
-moz-box-shadow: #333 0px 0px 5px;
box-shadow: #333 0px 0px 5px;
@@ -85,6 +86,7 @@ ul.menu li#active a
div.titlebox, #bpscredits, .ticket_menu{
-moz-border-radius: 1em;
-webkit-border-radius: 1em;
+ border-radius: 1em;
margin: 0.5em;
background-color: #fff;
padding-top: 1em;
@@ -336,6 +338,7 @@ input[type=submit], input[type=button], button, #paging a {
padding-right: 0.6em;
-moz-border-radius: 0.5em;
-webkit-border-radius: 0.5em;
+ border-radius: 0.5em;
background-color: #006699;
color: #fff;
}
@@ -353,6 +356,7 @@ form {
border-bottom: 1px solid black;
-moz-border-radius-bottomleft: 1em;
-webkit-border-bottom-left-radius: 1em;
+ border-bottom-left-radius: 1em;
padding: 0.5em;
background-color: #fff;
}
diff --git a/rt/share/html/m/_elements/wrapper b/rt/share/html/m/_elements/wrapper
index 794385db4..1891079bd 100644
--- a/rt/share/html/m/_elements/wrapper
+++ b/rt/share/html/m/_elements/wrapper
@@ -3,7 +3,7 @@ $title => ''
$show_home_button => 1
</%args>
<%init>
-if ($m->request_args->{'NotMobile'}) {
+if ($DECODED_ARGS->{'NotMobile'}) {
$session{'NotMobile'} = 1;
RT::Interface::Web::Redirect(RT->Config->Get('WebURL'));
$m->abort();
diff --git a/rt/t/api/config.t b/rt/t/api/config.t
index a986c3c4f..62b77dffa 100644
--- a/rt/t/api/config.t
+++ b/rt/t/api/config.t
@@ -1,7 +1,8 @@
use strict;
use warnings;
use RT;
-use RT::Test nodb => 1, tests => 9;
+use RT::Test nodb => 1, tests => 11;
+use Test::Warn;
ok(
RT::Config->AddOption(
@@ -31,3 +32,12 @@ is( $meta->{Widget}, '/Widgets/Form/Boolean', 'widget is updated to boolean' );
ok( RT::Config->DeleteOption( Name => 'foo' ), 'removed option foo' );
is( RT::Config->Meta('foo'), undef, 'foo is indeed deleted' );
+# Test EmailInputEncodings PostLoadCheck code
+RT::Config->Set('EmailInputEncodings', qw(utf-8 iso-8859-1 us-ascii foo));
+my @encodings = qw(utf-8-strict iso-8859-1 ascii);
+
+warning_is {RT::Config->PostLoadCheck} "Unknown encoding 'foo' in \@EmailInputEncodings option",
+ 'Correct warning for encoding foo';
+
+my @canonical_encodings = RT::Config->Get('EmailInputEncodings');
+is_deeply(\@encodings, \@canonical_encodings, 'Got correct encoding list');
diff --git a/rt/t/api/template-insert.t b/rt/t/api/template-insert.t
deleted file mode 100644
index 1bf5fc390..000000000
--- a/rt/t/api/template-insert.t
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/perl
-
-use warnings;
-use strict;
-
-
-use RT;
-use RT::Test tests => 7;
-
-
-
-# This tiny little test script triggers an interaction bug between DBD::Oracle 1.16, SB 1.15 and RT 3.4
-
-use_ok('RT::Template');
-my $template = RT::Template->new(RT->SystemUser);
-
-isa_ok($template, 'RT::Template');
-my ($val,$msg) = $template->Create(Queue => 1,
- Name => 'InsertTest',
- Content => 'This is template content');
-ok($val,$msg);
-is($template->Name, 'InsertTest');
-is($template->Content, 'This is template content', "We created the object right");
-($val, $msg) = $template->SetContent( 'This is new template content');
-ok($val,$msg);
-is($template->Content, 'This is new template content', "We managed to _Set_ the content");
diff --git a/rt/t/api/template-simple.t b/rt/t/api/template-simple.t
deleted file mode 100644
index bbdebb31f..000000000
--- a/rt/t/api/template-simple.t
+++ /dev/null
@@ -1,275 +0,0 @@
-use strict;
-use warnings;
-use RT;
-use RT::Test tests => 231;
-use Test::Warn;
-
-my $queue = RT::Queue->new(RT->SystemUser);
-$queue->Load("General");
-
-my $ticket_cf = RT::CustomField->new(RT->SystemUser);
-$ticket_cf->Create(
- Name => 'Department',
- Queue => '0',
- Type => 'FreeformSingle',
-);
-
-my $txn_cf = RT::CustomField->new(RT->SystemUser);
-$txn_cf->Create(
- Name => 'Category',
- LookupType => RT::Transaction->CustomFieldLookupType,
- Type => 'FreeformSingle',
-);
-$txn_cf->AddToObject($queue);
-
-my $ticket = RT::Ticket->new(RT->SystemUser);
-my ($id, $msg) = $ticket->Create(
- Subject => "template testing",
- Queue => "General",
- Owner => 'root@localhost',
- Requestor => ["dom\@example.com"],
- "CustomField-" . $txn_cf->id => "Special",
-);
-ok($id, "Created ticket: $msg");
-my $txn = $ticket->Transactions->First;
-
-$ticket->AddCustomFieldValue(
- Field => 'Department',
- Value => 'Coolio',
-);
-
-TemplateTest(
- Content => "\ntest",
- PerlOutput => "test",
- SimpleOutput => "test",
-);
-
-TemplateTest(
- Content => "\ntest { 5 * 5 }",
- PerlOutput => "test 25",
- SimpleOutput => "test { 5 * 5 }",
-);
-
-TemplateTest(
- Content => "\ntest { \$Requestor }",
- PerlOutput => "test dom\@example.com",
- SimpleOutput => "test dom\@example.com",
-);
-
-TemplateTest(
- Content => "\ntest { \$TicketSubject }",
- PerlOutput => "test ",
- SimpleOutput => "test template testing",
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TicketQueueId }",
- Output => "test 1",
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TicketQueueName }",
- Output => "test General",
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TicketOwnerId }",
- Output => "test 12",
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TicketOwnerName }",
- Output => "test root",
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TicketOwnerEmailAddress }",
- Output => "test root\@localhost",
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TicketStatus }",
- Output => "test new",
-);
-
-SimpleTemplateTest(
- Content => "\ntest #{ \$TicketId }",
- Output => "test #" . $ticket->id,
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TicketCFDepartment }",
- Output => "test Coolio",
-);
-
-SimpleTemplateTest(
- Content => "\ntest #{ \$TransactionId }",
- Output => "test #" . $txn->id,
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TransactionType }",
- Output => "test Create",
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TransactionCFCategory }",
- Output => "test Special",
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$TicketDelete }",
- Output => "test { \$TicketDelete }",
-);
-
-SimpleTemplateTest(
- Content => "\ntest { \$Nonexistent }",
- Output => "test { \$Nonexistent }",
-);
-
-warning_like {
- TemplateTest(
- Content => "\ntest { \$Ticket->Nonexistent }",
- PerlOutput => undef,
- SimpleOutput => "test { \$Ticket->Nonexistent }",
- );
-} qr/RT::Ticket::Nonexistent Unimplemented/;
-
-warning_like {
- TemplateTest(
- Content => "\ntest { \$Nonexistent->Nonexistent }",
- PerlOutput => undef,
- SimpleOutput => "test { \$Nonexistent->Nonexistent }",
- );
-} qr/Can't call method "Nonexistent" on an undefined value/;
-
-TemplateTest(
- Content => "\ntest { \$Ticket->OwnerObj->Name }",
- PerlOutput => "test root",
- SimpleOutput => "test { \$Ticket->OwnerObj->Name }",
-);
-
-warning_like {
- TemplateTest(
- Content => "\ntest { *!( }",
- SyntaxError => 1,
- PerlOutput => undef,
- SimpleOutput => "test { *!( }",
- );
-} qr/Template parsing error: syntax error/;
-
-TemplateTest(
- Content => "\ntest { \$rtname ",
- SyntaxError => 1,
- PerlOutput => undef,
- SimpleOutput => undef,
-);
-
-is($ticket->Status, 'new', "test setup");
-SimpleTemplateTest(
- Content => "\ntest { \$Ticket->SetStatus('resolved') }",
- Output => "test { \$Ticket->SetStatus('resolved') }",
-);
-is($ticket->Status, 'new', "simple templates can't call ->SetStatus");
-
-# Make sure changing the template's type works
-my $template = RT::Template->new(RT->SystemUser);
-$template->Create(
- Name => "type chameleon",
- Type => "Perl",
- Content => "\ntest { 10 * 7 }",
-);
-ok($id = $template->id, "Created template");
-$template->Parse;
-is($template->MIMEObj->stringify_body, "test 70", "Perl output");
-
-$template = RT::Template->new(RT->SystemUser);
-$template->Load($id);
-is($template->Name, "type chameleon");
-
-$template->SetType('Simple');
-$template->Parse;
-is($template->MIMEObj->stringify_body, "test { 10 * 7 }", "Simple output");
-
-$template = RT::Template->new(RT->SystemUser);
-$template->Load($id);
-is($template->Name, "type chameleon");
-
-$template->SetType('Perl');
-$template->Parse;
-is($template->MIMEObj->stringify_body, "test 70", "Perl output");
-
-undef $ticket;
-
-my $counter = 0;
-sub IndividualTemplateTest {
- local $Test::Builder::Level = $Test::Builder::Level + 1;
-
- my %args = (
- Name => "Test-" . ++$counter,
- Type => "Perl",
- @_,
- );
-
- my $t = RT::Template->new(RT->SystemUser);
- $t->Create(
- Name => $args{Name},
- Type => $args{Type},
- Content => $args{Content},
- );
-
- ok($t->id, "Created $args{Type} template");
- is($t->Name, $args{Name}, "$args{Type} template name");
- is($t->Content, $args{Content}, "$args{Type} content");
- is($t->Type, $args{Type}, "template type");
-
- # this should never blow up!
- my ($ok, $msg) = $t->CompileCheck;
-
- # we don't need to syntax check simple templates since if you mess them up
- # it's safe to just use the input directly as the template's output
- if ($args{SyntaxError} && $args{Type} eq 'Perl') {
- ok(!$ok, "got a syntax error");
- }
- else {
- ok($ok, $msg);
- }
-
- ($ok, $msg) = $t->Parse(
- TicketObj => $ticket,
- TransactionObj => $txn,
- );
- if (defined $args{Output}) {
- ok($ok, $msg);
- is($t->MIMEObj->stringify_body, $args{Output}, "$args{Type} template's output");
- }
- else {
- ok(!$ok, "expected a failure");
- }
-}
-
-sub TemplateTest {
- local $Test::Builder::Level = $Test::Builder::Level + 1;
- my %args = @_;
-
- for my $type ('Perl', 'Simple') {
- next if $args{"Skip$type"};
-
- IndividualTemplateTest(
- %args,
- Type => $type,
- Output => $args{$type . 'Output'},
- );
- }
-}
-
-sub SimpleTemplateTest {
- local $Test::Builder::Level = $Test::Builder::Level + 1;
- my %args = @_;
-
- IndividualTemplateTest(
- %args,
- Type => 'Simple',
- );
-}
-
diff --git a/rt/t/api/template.t b/rt/t/api/template.t
index 2fadede38..331d9f996 100644
--- a/rt/t/api/template.t
+++ b/rt/t/api/template.t
@@ -1,25 +1,34 @@
-use strict;
use warnings;
-use RT;
-use RT::Test tests => 2;
-
-
-{
-
-ok(require RT::Template);
+use strict;
+use RT;
+use RT::Test tests => 10;
-}
+my $queue = RT::Test->load_or_create_queue( Name => 'Templates' );
+ok $queue && $queue->id, "loaded or created a queue";
{
-
-my $t = RT::Template->new(RT->SystemUser);
-$t->Create(Name => "Foo", Queue => 1);
-my $t2 = RT::Template->new(RT->Nobody);
-$t2->Load($t->Id);
-ok($t2->QueueObj->id, "Got the template's queue objet");
-
-
+ my $template = RT::Template->new( RT->SystemUser );
+ isa_ok($template, 'RT::Template');
+ my ($val,$msg) = $template->Create(
+ Queue => $queue->id,
+ Name => 'InsertTest',
+ Content => 'This is template content'
+ );
+ ok $val, "created a template" or diag "error: $msg";
+ ok my $id = $template->id, "id is defined";
+ is $template->Name, 'InsertTest';
+ is $template->Content, 'This is template content', "We created the object right";
+
+ ($val, $msg) = $template->SetContent( 'This is new template content');
+ ok $val, "changed content" or diag "error: $msg";
+
+ is $template->Content, 'This is new template content', "We managed to _Set_ the content";
+
+ ($val, $msg) = $template->Delete;
+ ok $val, "deleted template";
+
+ $template->Load($id);
+ ok !$template->id, "can not load template after deletion";
}
-
diff --git a/rt/t/articles/search-interface.t b/rt/t/articles/search-interface.t
index eb3a4f763..bcba3116b 100644
--- a/rt/t/articles/search-interface.t
+++ b/rt/t/articles/search-interface.t
@@ -3,7 +3,7 @@
use strict;
use warnings;
-use RT::Test tests => 23;
+use RT::Test tests => 44;
use RT::CustomField;
use RT::Queue;
@@ -67,7 +67,12 @@ my %cvals = ('article1q' => 'Some question about swallows',
'article3q' => 'Why should I eat my supper?',
'article3a' => 'There are starving children in Africa',
'article4q' => 'What did Brian originally write?',
- 'article4a' => 'Romanes eunt domus');
+ 'article4a' => 'This is an answer that is longer than 255 '
+ . 'characters so these tests will be sure to use the LargeContent '
+ . 'SQL as well as the normal SQL that would be generated if this '
+ . 'was an answer that was shorter than 255 characters. This second '
+ . 'sentence has a few extra characters to get this string to go '
+ . 'over the 255 character boundary. Lorem ipsum.');
# Create an article or two with our custom field values.
@@ -108,6 +113,52 @@ isa_ok($m, 'Test::WWW::Mechanize');
ok($m->login, 'logged in');
$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! },
'UI -> Articles' );
-$m->follow_link_ok( {text => 'Search'}, 'Articles -> Search');
-$m->follow_link_ok( {text => 'in class '.$class->Name}, 'Articles in class '.$class->Name);
-$m->content_contains($article1->Name);
+
+# In all of the search results below, the results page should
+# have the summary text of the article it occurs in.
+
+# Case sensitive search on small field.
+DoArticleSearch($m, $class->Name, 'Africa');
+$m->text_contains('Search results'); # Did we do a search?
+$m->text_contains('blah blah 1');
+
+# Case insensitive search on small field.
+DoArticleSearch($m, $class->Name, 'africa');
+$m->text_contains('Search results'); # Did we do a search?
+$m->text_contains('blah blah 1');
+
+# Case sensitive search on large field.
+DoArticleSearch($m, $class->Name, 'ipsum');
+$m->text_contains('Search results'); # Did we do a search?
+$m->text_contains('hoi polloi 4');
+
+# Case insensitive search on large field.
+DoArticleSearch($m, $class->Name, 'lorem');
+$m->text_contains('Search results'); # Did we do a search?
+TODO:{
+ local $TODO = 'Case insensitive search on LONGBLOB not available in MySQL'
+ if RT->Config->Get('DatabaseType') eq 'mysql';
+ $m->text_contains('hoi polloi 4');
+}
+
+# When you send $m to this sub, it must be on a page with
+# a Search link.
+sub DoArticleSearch{
+ my $m = shift;
+ my $class_name = shift;
+ my $search_text = shift;
+
+ $m->follow_link_ok( {text => 'Search'}, 'Articles -> Search');
+ $m->follow_link_ok( {text => 'in class '. $class_name}, 'Articles in class '. $class_name);
+ $m->text_contains('First article');
+
+ $m->submit_form_ok( {
+ form_number => 3,
+ fields => {
+ 'Article~' => $search_text
+ },
+ }, "Search for $search_text"
+ );
+ return;
+}
+
diff --git a/rt/t/articles/uri-a.t b/rt/t/articles/uri-a.t
index 82d0f1b01..5c1fdaf36 100644
--- a/rt/t/articles/uri-a.t
+++ b/rt/t/articles/uri-a.t
@@ -3,7 +3,7 @@
use strict;
use warnings;
-use RT::Test tests => 7;
+use RT::Test tests => 15;
use_ok("RT::URI::a");
my $uri = RT::URI::a->new($RT::SystemUser);
@@ -26,3 +26,39 @@ is(ref($uri->Object), "RT::Article", "Object loaded is an article");
is($uri->Object->Id, $article->Id, "Object loaded has correct ID");
is($article->URI, 'fsck.com-article://example.com/article/'.$article->Id,
"URI object has correct URI string");
+
+{
+ my $aid = $article->id;
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ my ($id, $msg) = $ticket->Create(
+ Queue => 1,
+ Subject => 'test ticket',
+ );
+ ok $id, "Created a test ticket";
+
+ # Try searching
+ my $tickets = RT::Tickets->new( RT->SystemUser );
+ $tickets->FromSQL(" RefersTo = 'a:$aid' ");
+ is $tickets->Count, 0, "No results yet";
+
+ # try with the full uri
+ $tickets->FromSQL(" RefersTo = '@{[ $article->URI ]}' ");
+ is $tickets->Count, 0, "Still no results";
+
+ # add the link
+ $ticket->AddLink( Type => 'RefersTo', Target => "a:$aid" );
+
+ # verify the ticket has it
+ my @links = @{$ticket->RefersTo->ItemsArrayRef};
+ is scalar @links, 1, "Has one RefersTo link";
+ is ref $links[0]->TargetObj, "RT::Article", "Link points to an article";
+ is $links[0]->TargetObj->id, $aid, "Link points to the article we specified";
+
+ # search again
+ $tickets->FromSQL(" RefersTo = 'a:$aid' ");
+ is $tickets->Count, 1, "Found one ticket with short URI";
+
+ # search with the full uri
+ $tickets->FromSQL(" RefersTo = '@{[ $article->URI ]}' ");
+ is $tickets->Count, 1, "Found one ticket with full URI";
+}
diff --git a/rt/t/data/configs/apache2.2+fastcgi.conf.in b/rt/t/data/configs/apache2.2+fastcgi.conf.in
index 3ec36dd0f..03eaa9a70 100644
--- a/rt/t/data/configs/apache2.2+fastcgi.conf.in
+++ b/rt/t/data/configs/apache2.2+fastcgi.conf.in
@@ -12,6 +12,7 @@ Group @WEB_GROUP@
</IfModule>
</IfModule>
+ServerName localhost
Listen %%LISTEN%%
ErrorLog "%%LOG_FILE%%"
diff --git a/rt/t/data/configs/apache2.2+mod_perl.conf.in b/rt/t/data/configs/apache2.2+mod_perl.conf.in
index 3b1f3f618..20d2f44e5 100644
--- a/rt/t/data/configs/apache2.2+mod_perl.conf.in
+++ b/rt/t/data/configs/apache2.2+mod_perl.conf.in
@@ -30,6 +30,7 @@ Group @WEB_GROUP@
</IfModule>
</IfModule>
+ServerName localhost
Listen %%LISTEN%%
ErrorLog "%%LOG_FILE%%"
diff --git a/rt/t/mail/dashboard-chart-with-utf8.t b/rt/t/mail/dashboard-chart-with-utf8.t
index 6d07b963b..79f5f0e11 100644
--- a/rt/t/mail/dashboard-chart-with-utf8.t
+++ b/rt/t/mail/dashboard-chart-with-utf8.t
@@ -1,7 +1,17 @@
use strict;
use warnings;
-use RT::Test tests => 15;
+BEGIN {
+ require RT::Test;
+
+ if (eval { require GD }) {
+ RT::Test->import(tests => 15);
+ }
+ else {
+ RT::Test->import(skip_all => 'GD required.');
+ }
+}
+
use utf8;
my $root = RT::Test->load_or_create_user( Name => 'root' );
diff --git a/rt/t/mail/dashboards.t b/rt/t/mail/dashboards.t
index 7a7a54ce6..00cfc6acd 100644
--- a/rt/t/mail/dashboards.t
+++ b/rt/t/mail/dashboards.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
-use RT::Test tests => 187;
+use RT::Test tests => 181;
use Test::Warn;
use RT::Dashboard::Mailer;
@@ -138,17 +138,6 @@ sub delete_dashboard { # {{{
ok($ok, $msg);
} # }}}
-sub delete_subscriptions { # {{{
- my $subscription_id = shift;
- # delete the dashboard and make sure we get exactly one subscription failure
- # notice
- my $user = RT::User->new(RT->SystemUser);
- $user->Load('root');
- for my $subscription ($user->Attributes->Named('Subscription')) {
- $subscription->Delete;
- }
-} # }}}
-
my $good_time = 1290423660; # 6:01 EST on a monday
my $bad_time = 1290427260; # 7:01 EST on a monday
@@ -223,21 +212,9 @@ SKIP: {
delete_dashboard($dashboard_id);
-warning_like {
- RT::Dashboard::Mailer->MailDashboards(All => 1);
-} qr/Unable to load dashboard $dashboard_id of subscription $subscription_id for user root/;
-
-@mails = RT::Test->fetch_caught_mails;
-is(@mails, 1, "one mail for subscription failure");
-$mail = parse_mail($mails[0]);
-is($mail->head->get('Subject'), "[example.com] Missing dashboard!\n");
-is($mail->head->get('From'), "dashboard\@example.com\n");
-is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n");
-is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n");
-
RT::Dashboard::Mailer->MailDashboards(All => 1);
@mails = RT::Test->fetch_caught_mails;
-is(@mails, 0, "no mail because the subscription notice happens only once");
+is(@mails, 0, "no mail because the subscription is deleted");
RT::Test->stop_server;
RT::Test->clean_caught_mails;
@@ -277,7 +254,6 @@ RT->Config->Set('EmailDashboardRemove' => (qr/My dashboards/, "Testing!"));
($baseurl, $m) = RT::Test->started_ok;
delete_dashboard($dashboard_id);
-delete_subscriptions();
RT::Test->clean_caught_mails;
@@ -330,7 +306,6 @@ RT->Config->Set('EmailDashboardRemove' => (qr/My dashboards/, "Testing!"));
($baseurl, $m) = RT::Test->started_ok;
delete_dashboard($dashboard_id);
-delete_subscriptions();
RT::Test->clean_caught_mails;
@@ -373,7 +348,6 @@ RT->Config->Set('EmailDashboardRemove' => (qr/My dashboards/, "Testing!"));
($baseurl, $m) = RT::Test->started_ok;
delete_dashboard($dashboard_id);
-delete_subscriptions();
RT::Test->clean_caught_mails;
diff --git a/rt/t/mail/gateway.t b/rt/t/mail/gateway.t
index 9f0e669a3..98eabd56e 100644
--- a/rt/t/mail/gateway.t
+++ b/rt/t/mail/gateway.t
@@ -57,7 +57,7 @@ use strict;
use warnings;
-use RT::Test config => 'Set( $UnsafeEmailCommands, 1);', tests => 221, actual_server => 1;
+use RT::Test config => 'Set( $UnsafeEmailCommands, 1);', tests => 228, actual_server => 1;
my ($baseurl, $m) = RT::Test->started_ok;
use RT::Tickets;
@@ -608,6 +608,35 @@ EOF
$m->no_warnings_ok;
}
+diag "make sure we check that UTF-8 is really UTF-8";
+{
+ my $text = <<EOF;
+From: root\@localhost
+To: rtemail\@@{[RT->Config->Get('rtname')]}
+Subject: This is test wrong utf-8 chars
+Content-Type: text/plain; charset="utf-8"
+
+utf-8: informaci\303\263n confidencial
+latin1: informaci\363n confidencial
+
+bye
+EOF
+ my ($status, $id) = RT::Test->send_via_mailgate_and_http($text);
+ is ($status >> 8, 0, "The mail gateway exited normally");
+ ok ($id, "created ticket");
+
+ my $tick = RT::Test->last_ticket;
+ is ($tick->Id, $id, "correct ticket");
+
+ my $content = $tick->Transactions->First->Content;
+ Encode::_utf8_off($content);
+
+ like $content, qr{informaci\303\263n confidencial};
+ like $content, qr{informaci\357\277\275n confidencial};
+
+ $m->no_warnings_ok;
+}
+
diag "check that mailgate doesn't suffer from empty Reply-To:";
{
my $text = <<EOF;
diff --git a/rt/t/shredder/01ticket.t b/rt/t/shredder/01ticket.t
index 7dff16df3..a7abeef6e 100644
--- a/rt/t/shredder/01ticket.t
+++ b/rt/t/shredder/01ticket.t
@@ -78,7 +78,11 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"
my $shredder = shredder_new();
$shredder->PutObjects( Objects => $child );
$shredder->WipeoutAll;
- cmp_deeply( dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint");
+
+ TODO: {
+ local $TODO = "Shredder doesn't delete all links and transactions";
+ cmp_deeply( dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint");
+ }
$shredder->PutObjects( Objects => $parent );
$shredder->WipeoutAll;
diff --git a/rt/t/shredder/03plugin_tickets.t b/rt/t/shredder/03plugin_tickets.t
index 092b57052..e63eef8fd 100644
--- a/rt/t/shredder/03plugin_tickets.t
+++ b/rt/t/shredder/03plugin_tickets.t
@@ -34,6 +34,7 @@ use_ok('RT::Tickets');
my $child = RT::Ticket->new( RT->SystemUser );
my ($cid) = $child->Create( Subject => 'child', Queue => 1, MemberOf => $pid );
ok( $cid, "created new ticket" );
+ $_->ApplyTransactionBatch for $parent, $child;
my $plugin = RT::Shredder::Plugin::Tickets->new;
isa_ok($plugin, 'RT::Shredder::Plugin::Tickets');
@@ -77,6 +78,8 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"
my ($status, $msg) = $child->AddLink( Target => $pid, Type => 'DependsOn' );
ok($status, "added reqursive link") or diag "error: $msg";
+ $_->ApplyTransactionBatch for $parent, $child;
+
my $plugin = RT::Shredder::Plugin::Tickets->new;
isa_ok($plugin, 'RT::Shredder::Plugin::Tickets');
@@ -121,6 +124,8 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"
ok( $cid2, "created new ticket" );
$child2->SetStatus('resolved');
+ $_->ApplyTransactionBatch for $parent, $child1, $child2;
+
my $plugin = RT::Shredder::Plugin::Tickets->new;
isa_ok($plugin, 'RT::Shredder::Plugin::Tickets');
diff --git a/rt/t/shredder/03plugin_users.t b/rt/t/shredder/03plugin_users.t
index 4f4ecc89c..1f4cb4934 100644
--- a/rt/t/shredder/03plugin_users.t
+++ b/rt/t/shredder/03plugin_users.t
@@ -5,8 +5,8 @@ use warnings;
use Test::Deep;
use File::Spec;
-use Test::More tests => 9;
-use RT::Test nodb => 1;
+use Test::More tests => 21;
+use RT::Test ();
BEGIN {
my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
File::Spec->curdir());
@@ -38,3 +38,61 @@ use_ok('RT::Shredder::Plugin::Users');
ok(!$status, "bad 'status' arg value");
}
+init_db();
+
+RT::Test->set_rights(
+ { Principal => 'Everyone', Right => [qw(CreateTicket)] },
+);
+
+create_savepoint('clean');
+
+{ # Create two users and a ticket. Shred second user and replace relations with first user
+ my ($uidA, $uidB, $msg);
+ my $userA = RT::User->new( RT->SystemUser );
+ ($uidA, $msg) = $userA->Create( Name => 'userA', Privileged => 1, Disabled => 0 );
+ ok( $uidA, "created user A" ) or diag "error: $msg";
+
+ my $userB = RT::User->new( RT->SystemUser );
+ ($uidB, $msg) = $userB->Create( Name => 'userB', Privileged => 1, Disabled => 0 );
+ ok( $uidB, "created user B" ) or diag "error: $msg";
+
+ my ($tid, $trid);
+ my $ticket = RT::Ticket->new( RT::CurrentUser->new($userB) );
+ ($tid, $trid, $msg) = $ticket->Create( Subject => 'UserB Ticket', Queue => 1 );
+ ok( $tid, "created new ticket") or diag "error: $msg";
+
+ my $transaction = RT::Transaction->new( RT->SystemUser );
+ $transaction->Load($trid);
+ is ( $transaction->Creator, $uidB, "ticket creator is user B" );
+
+ my $plugin = RT::Shredder::Plugin::Users->new;
+ isa_ok($plugin, 'RT::Shredder::Plugin::Users');
+
+ my $status;
+ ($status, $msg) = $plugin->TestArgs( status => 'any', name => 'userB', replace_relations => $uidA );
+ ok($status, "plugin arguments are ok") or diag "error: $msg";
+
+ my @objs;
+ ($status, @objs) = $plugin->Run;
+ ok($status, "executed plugin successfully") or diag "error: @objs";
+ @objs = RT::Shredder->CastObjectsToRecords( Objects => \@objs );
+ is(scalar @objs, 1, "one object in the result set");
+
+ my $shredder = shredder_new();
+
+ ($status, $msg) = $plugin->SetResolvers( Shredder => $shredder );
+ ok($status, "set conflicts resolver") or diag "error: $msg";
+
+ $shredder->PutObjects( Objects => \@objs );
+ $shredder->WipeoutAll;
+
+ $ticket->Load( $tid );
+ is($ticket->id, $tid, 'loaded ticket');
+
+ $transaction->Load($trid);
+ is ( $transaction->Creator, $uidA, "ticket creator is now user A" );
+
+ $shredder->Wipeout( Object => $ticket );
+ $shredder->Wipeout( Object => $userA );
+}
+cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
diff --git a/rt/t/shredder/utils.pl b/rt/t/shredder/utils.pl
index 5f5c1822f..9b848c662 100644
--- a/rt/t/shredder/utils.pl
+++ b/rt/t/shredder/utils.pl
@@ -283,7 +283,7 @@ sub dump_sqlite
my $old_fhkn = $dbh->{'FetchHashKeyName'};
$dbh->{'FetchHashKeyName'} = 'NAME_lc';
- my $sth = $dbh->table_info( '', '', '%', 'TABLE' ) || die $DBI::err;
+ my $sth = $dbh->table_info( '', '%', '%', 'TABLE' ) || die $DBI::err;
my @tables = keys %{$sth->fetchall_hashref( 'table_name' )};
my $res = {};
diff --git a/rt/t/ticket/search_by_watcher.t b/rt/t/ticket/search_by_watcher.t
index 809450b56..cfc7b1c22 100644
--- a/rt/t/ticket/search_by_watcher.t
+++ b/rt/t/ticket/search_by_watcher.t
@@ -142,8 +142,8 @@ sub run_auto_tests {
@conditions = (
'Cc = "not@exist"' => sub { 0 },
'Cc != "not@exist"' => sub { 1 },
- 'Cc IS NULL' => sub { $_[0] =~ 'c:-;' },
- 'Cc IS NOT NULL' => sub { $_[0] !~ 'c:-;' },
+ 'Cc IS NULL' => sub { $_[0] =~ /c:-;/ },
+ 'Cc IS NOT NULL' => sub { $_[0] !~ /c:-;/ },
'Cc = "x@foo.com"' => sub { $_[0] =~ /c:[^;]*x/ },
'Cc != "x@foo.com"' => sub { $_[0] !~ /c:[^;]*x/ },
'Cc LIKE "@bar.com"' => sub { $_[0] =~ /c:[^;]*(?:y|z)/ },
@@ -152,8 +152,8 @@ sub run_auto_tests {
'Requestor = "not@exist"' => sub { 0 },
'Requestor != "not@exist"' => sub { 1 },
- 'Requestor IS NULL' => sub { $_[0] =~ 'r:-;' },
- 'Requestor IS NOT NULL' => sub { $_[0] !~ 'r:-;' },
+ 'Requestor IS NULL' => sub { $_[0] =~ /r:-;/ },
+ 'Requestor IS NOT NULL' => sub { $_[0] !~ /r:-;/ },
'Requestor = "x@foo.com"' => sub { $_[0] =~ /r:[^;]*x/ },
'Requestor != "x@foo.com"' => sub { $_[0] !~ /r:[^;]*x/ },
'Requestor LIKE "@bar.com"' => sub { $_[0] =~ /r:[^;]*(?:y|z)/ },
@@ -174,7 +174,7 @@ sub run_auto_tests {
'Subject LIKE "ne"' => sub { 0 },
'Subject NOT LIKE "ne"' => sub { 1 },
'Subject = "r:x;c:y;"' => sub { $_[0] eq 'r:x;c:y;' },
- 'Subject LIKE "x"' => sub { $_[0] =~ 'x' },
+ 'Subject LIKE "x"' => sub { $_[0] =~ /x/ },
);
@tickets = generate_tix();
diff --git a/rt/t/web/attachments.t b/rt/t/web/attachments.t
index 8c75f6caf..784cbbe88 100644
--- a/rt/t/web/attachments.t
+++ b/rt/t/web/attachments.t
@@ -1,10 +1,11 @@
#!/usr/bin/perl -w
use strict;
-use RT::Test tests => 25;
+use RT::Test tests => 33;
use constant LogoFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png';
use constant FaviconFile => $RT::MasonComponentRoot .'/NoAuth/images/favicon.png';
+use constant TextFile => $RT::MasonComponentRoot .'/NoAuth/css/print.css';
my ($baseurl, $m) = RT::Test->started_ok;
ok $m->login, 'logged in';
@@ -30,9 +31,18 @@ $m->content_contains('Attachments test', 'we have subject on the page');
$m->content_contains('Some content', 'and content');
$m->content_contains('Download bpslogo.png', 'page has file name');
+open LOGO, "<", LogoFile or die "Can't open logo file: $!";
+binmode LOGO;
+my $logo_contents = do {local $/; <LOGO>};
+close LOGO;
+$m->follow_link_ok({text => "Download bpslogo.png"});
+is($m->content_type, "image/png");
+is($m->content, $logo_contents, "Binary content matches");
+
+$m->back;
$m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
$m->form_name('TicketUpdate');
-$m->field('Attach', LogoFile);
+$m->field('Attach', TextFile);
$m->click('AddMoreAttach');
is($m->status, 200, "request successful");
@@ -44,7 +54,16 @@ is($m->status, 200, "request successful");
$m->content_contains('Download bpslogo.png', 'page has file name');
$m->content_contains('Download favicon.png', 'page has file name');
+$m->content_contains('Download print.css', 'page has file name');
+
+$m->follow_link_ok( { text => 'Download bpslogo.png' } );
+is( $m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' );
+
+$m->back;
+$m->follow_link_ok( { text => 'Download print.css' } );
+is( $m->response->header('Content-Type'),
+ 'text/css;charset=UTF-8', 'Content-Type of text has charset' );
diag "test mobile ui";
$m->get_ok( $baseurl . '/m/ticket/create?Queue=' . $qid );
diff --git a/rt/t/web/command_line.t b/rt/t/web/command_line.t
index 1fed8e69e..394daaba9 100644
--- a/rt/t/web/command_line.t
+++ b/rt/t/web/command_line.t
@@ -3,7 +3,7 @@
use strict;
use File::Spec ();
use Test::Expect;
-use RT::Test tests => 303, actual_server => 1;
+use RT::Test tests => 315, actual_server => 1;
my ($baseurl, $m) = RT::Test->started_ok;
use RT::User;
@@ -480,6 +480,8 @@ expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in
expect_like(qr/Created link $link1_id $reln $link2_id/, 'Linked');
expect_send("show -s ticket/$link1_id/links", "Checking creation of $reln...");
expect_like(qr/$display_relns{$reln}: [\w\d\.\-]+:\/\/[\w\d\.]+\/ticket\/$link2_id/, "Created link $reln");
+ expect_send("show ticket/$link1_id/links", "Checking show links without format");
+ expect_like(qr/$display_relns{$reln}: [\w\d\.\-]+:\/\/[\w\d\.]+\/ticket\/$link2_id/, "Found link $reln");
# delete link
expect_send("link -d $link1_id $reln $link2_id", "Delete $reln...");
diff --git a/rt/t/web/command_line_with_unknown_field.t b/rt/t/web/command_line_with_unknown_field.t
index 736be4d1c..d63956be3 100644
--- a/rt/t/web/command_line_with_unknown_field.t
+++ b/rt/t/web/command_line_with_unknown_field.t
@@ -3,7 +3,7 @@
use strict;
use File::Spec ();
use Test::Expect;
-use RT::Test tests => 14, actual_server => 1;
+use RT::Test tests => 17, actual_server => 1;
my ($baseurl, $m) = RT::Test->started_ok;
my $rt_tool_path = "$RT::BinPath/rt";
@@ -19,6 +19,11 @@ expect_run(
prompt => 'rt> ',
quit => 'quit',
);
+
+expect_send( q{create -t ticket set foo=bar}, "create ticket with unknown field" );
+expect_like(qr/foo: Unknown field/, 'foo is unknown field');
+expect_like(qr/Could not create ticket/, 'ticket is not created');
+
expect_send(q{create -t ticket set subject='new ticket' add cc=foo@example.com}, "Creating a ticket...");
expect_like(qr/Ticket \d+ created/, "Created the ticket");
diff --git a/rt/t/web/crypt-gnupg.t b/rt/t/web/crypt-gnupg.t
index 6bdefdac7..8c0eb570d 100644
--- a/rt/t/web/crypt-gnupg.t
+++ b/rt/t/web/crypt-gnupg.t
@@ -53,6 +53,7 @@ RT::Test->clean_caught_mails;
$m->goto_create_ticket( $queue );
$m->form_name('TicketCreate');
+$m->field('Requestors', 'recipient@example.com');
$m->field('Subject', 'Encryption test');
$m->field('Content', 'Some content');
ok($m->value('Encrypt', 2), "encrypt tick box is checked");
@@ -122,6 +123,7 @@ RT::Test->clean_caught_mails;
$m->goto_create_ticket( $queue );
$m->form_name('TicketCreate');
+$m->field('Requestors', 'recipient@example.com');
$m->field('Subject', 'Signing test');
$m->field('Content', 'Some other content');
ok(!$m->value('Encrypt', 2), "encrypt tick box is unchecked");
@@ -195,6 +197,7 @@ RT::Test->clean_caught_mails;
$m->goto_create_ticket( $queue );
$m->form_name('TicketCreate');
+$m->field('Requestors', 'recipient@example.com');
$m->field('Subject', 'Crypt+Sign test');
$m->field('Content', 'Some final? content');
ok($m->value('Encrypt', 2), "encrypt tick box is checked");
@@ -260,6 +263,7 @@ RT::Test->clean_caught_mails;
$m->goto_create_ticket( $queue );
$m->form_name('TicketCreate');
+$m->field('Requestors', 'recipient@example.com');
$m->field('Subject', 'Test crypt-off on encrypted queue');
$m->field('Content', 'Thought you had me figured out didya');
$m->field(Encrypt => undef, 2); # turn off encryption
diff --git a/rt/t/web/googleish_search.t b/rt/t/web/googleish_search.t
index e2a4e9116..f4c8fa4b6 100644
--- a/rt/t/web/googleish_search.t
+++ b/rt/t/web/googleish_search.t
@@ -2,7 +2,8 @@
use strict;
use warnings;
-use RT::Test tests => 96, config => 'Set( %FullTextSearch, Enable => 1, Indexed => 0 );';
+use RT::Test tests => undef,
+ config => 'Set( %FullTextSearch, Enable => 1, Indexed => 0 );';
my ($baseurl, $m) = RT::Test->started_ok;
my $url = $m->rt_base_url;
@@ -57,6 +58,7 @@ ok $two_words_queue && $two_words_queue->id, 'loaded or created a queue';
is $parser->QueryToSQL("'me'"), "$active AND ( Subject LIKE 'me' )", "correct parsing";
is $parser->QueryToSQL("owner:me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing";
is $parser->QueryToSQL("owner:'me'"), "( Owner = 'me' ) AND $active", "correct parsing";
+ is $parser->QueryToSQL('owner:root@localhost'), "( Owner.EmailAddress = 'root\@localhost' ) AND $active", "Email address as owner";
is $parser->QueryToSQL("resolved me"), "( Owner.id = '__CurrentUser__' ) AND ( Status = 'resolved' )", "correct parsing";
is $parser->QueryToSQL("resolved active me"), "( Owner.id = '__CurrentUser__' ) AND ( Status = 'resolved' OR Status = 'new' OR Status = 'open' OR Status = 'stalled' )", "correct parsing";
@@ -217,3 +219,5 @@ for my $quote ( q{'}, q{"} ) {
}
}
+undef $m;
+done_testing;
diff --git a/rt/t/web/query_builder_queue_limits.t b/rt/t/web/query_builder_queue_limits.t
index a3b976524..f583d64cc 100644
--- a/rt/t/web/query_builder_queue_limits.t
+++ b/rt/t/web/query_builder_queue_limits.t
@@ -11,6 +11,9 @@ $lifecycles->{foo} = {
};
+# explicitly Set so RT::Test can catch our change
+RT->Config->Set( Lifecycles => %$lifecycles );
+
RT::Lifecycle->FillCache();
my $general = RT::Test->load_or_create_queue( Name => 'General' );
diff --git a/rt/t/web/search_simple.t b/rt/t/web/search_simple.t
index 1efc9a566..a1a3ce806 100644
--- a/rt/t/web/search_simple.t
+++ b/rt/t/web/search_simple.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test tests => 16;
+use RT::Test tests => 30;
my ( $baseurl, $m ) = RT::Test->started_ok;
RT::Test->create_tickets(
@@ -19,4 +19,58 @@ $m->content_contains( 'Show Results', "has page menu" );
$m->title_is( 'Found 1 ticket', 'title' );
$m->content_contains( 'ticket foo', 'has ticket foo' );
+# Test searches on custom fields
+my $cf1 = RT::Test->load_or_create_custom_field(
+ Name => 'Location',
+ Queue => 'General',
+ Type => 'FreeformSingle', );
+isa_ok( $cf1, 'RT::CustomField' );
+
+my $cf2 = RT::Test->load_or_create_custom_field(
+ Name => 'Server-name',
+ Queue => 'General',
+ Type => 'FreeformSingle', );
+isa_ok( $cf2, 'RT::CustomField' );
+
+my $t = RT::Ticket->new(RT->SystemUser);
+
+{
+ my ($id,undef,$msg) = $t->Create(
+ Queue => 'General',
+ Subject => 'Test searching CFs');
+ ok( $id, "Created ticket - $msg" );
+}
+
+{
+ my ($status, $msg) = $t->AddCustomFieldValue(
+ Field => $cf1->id,
+ Value => 'Downtown');
+ ok( $status, "Added CF value - $msg" );
+}
+
+{
+ my ($status, $msg) = $t->AddCustomFieldValue(
+ Field => $cf2->id,
+ Value => 'Proxy');
+ ok( $status, "Added CF value - $msg" );
+}
+
+# Regular search
+my $search = 'cf.Location:Downtown';
+$m->get_ok("/Search/Simple.html?q=$search");
+$m->title_is( 'Found 1 ticket', 'Found 1 ticket' );
+$m->text_contains( 'Test searching CFs', "Found test CF ticket with $search" );
+
+# Case insensitive
+$search = "cf.Location:downtown";
+$m->get_ok("/Search/Simple.html?q=$search");
+$m->title_is( 'Found 1 ticket', 'Found 1 ticket' );
+$m->text_contains( 'Test searching CFs', "Found test CF ticket with $search" );
+
+# With dash in CF name
+$search = "cf.Server-name:Proxy";
+$m->get_ok("/Search/Simple.html?q=$search");
+$m->title_is( 'Found 1 ticket', 'Found 1 ticket' );
+$m->text_contains( 'Test searching CFs', "Found test CF ticket with $search" );
+
# TODO more simple search tests
diff --git a/rt/t/web/ticket_modify_all.t b/rt/t/web/ticket_modify_all.t
index c9dd7e7cd..2f0c4d1b3 100644
--- a/rt/t/web/ticket_modify_all.t
+++ b/rt/t/web/ticket_modify_all.t
@@ -1,7 +1,7 @@
use strict;
use warnings;
-use RT::Test tests => 15;
+use RT::Test tests => 22;
my $ticket = RT::Test->create_ticket(
Subject => 'test bulk update',
@@ -40,5 +40,44 @@ $m->click('SubmitTicket');
$m->form_name('TicketModifyAll');
is($m->value('Owner'), 'root', 'owner was successfully changed to root');
-# XXX TODO test other parts, i.e. basic, dates, people and links
+$m->get_ok($url . "/Ticket/ModifyAll.html?id=" . $ticket->id);
+$m->form_name('TicketModifyAll');
+$m->field('Starts_Date' => "2013-01-01 00:00:00");
+$m->click('SubmitTicket');
+$m->text_contains("Starts: (Tue Jan 01 00:00:00 2013)", 'start date successfully updated');
+
+$m->form_name('TicketModifyAll');
+$m->field('Started_Date' => "2014-01-01 00:00:00");
+$m->click('SubmitTicket');
+$m->text_contains("Started: (Wed Jan 01 00:00:00 2014)", 'started date successfully updated');
+
+$m->form_name('TicketModifyAll');
+$m->field('Told_Date' => "2015-01-01 00:00:00");
+$m->click('SubmitTicket');
+$m->text_contains("Last Contact: (Thu Jan 01 00:00:00 2015)", 'told date successfully updated');
+
+$m->form_name('TicketModifyAll');
+$m->field('Due_Date' => "2016-01-01 00:00:00");
+$m->click('SubmitTicket');
+$m->text_contains("Due: (Fri Jan 01 00:00:00 2016)", 'due date successfully updated');
+
+$m->get( $url . '/Ticket/ModifyAll.html?id=' . $ticket->id );
+$m->form_name('TicketModifyAll');
+$m->field(WatcherTypeEmail => 'Requestor');
+$m->field(WatcherAddressEmail => 'root@localhost');
+$m->click('SubmitTicket');
+$m->text_contains(
+ "Added principal as a Requestor for this ticket",
+ 'watcher is added',
+);
+$m->form_name('TicketModifyAll');
+$m->field(WatcherTypeEmail => 'Requestor');
+$m->field(WatcherAddressEmail => 'root@localhost');
+$m->click('SubmitTicket');
+$m->text_contains(
+ "That principal is already a Requestor for this ticket",
+ 'no duplicate watchers',
+);
+
+# XXX TODO test other parts, i.e. links
diff --git a/rt/t/web/transaction_batch.t b/rt/t/web/transaction_batch.t
index ae04e1fca..12d01fba4 100644
--- a/rt/t/web/transaction_batch.t
+++ b/rt/t/web/transaction_batch.t
@@ -12,7 +12,14 @@ my ($val, $msg) =$s1->Create( Queue => $q->Id,
ScripAction => 'User Defined',
CustomIsApplicableCode => 'return ($self->TransactionObj->Field||"") eq "TimeEstimated"',
CustomPrepareCode => 'return 1',
- CustomCommitCode => '$self->TicketObj->SetPriority($self->TicketObj->Priority + 2); return 1;',
+ CustomCommitCode => '
+if ( $self->TicketObj->CurrentUser->Name ne "RT_System" ) {
+ warn "Ticket obj has incorrect CurrentUser (should be RT_System) ".$self->TicketObj->CurrentUser->Name
+}
+if ( $self->TicketObj->QueueObj->CurrentUser->Name ne "RT_System" ) {
+ warn "Queue obj has incorrect CurrentUser (should be RT_System) ".$self->TicketObj->QueueObj->CurrentUser->Name
+}
+$self->TicketObj->SetPriority($self->TicketObj->Priority + 2); return 1;',
Template => 'Blank',
Stage => 'TransactionBatch',
);