rt 4.0.6
authorIvan Kohler <ivan@freeside.biz>
Thu, 7 Jun 2012 23:55:45 +0000 (16:55 -0700)
committerIvan Kohler <ivan@freeside.biz>
Thu, 7 Jun 2012 23:55:45 +0000 (16:55 -0700)
172 files changed:
FS/FS/Mason.pm
rt/Makefile.in
rt/bin/rt
rt/bin/rt-mailgate.in
rt/bin/rt.in
rt/configure
rt/docs/UPGRADING-4.0
rt/docs/hacking.pod
rt/docs/security.pod
rt/docs/web_deployment.pod
rt/etc/RT_Config.pm.in
rt/etc/schema.mysql
rt/etc/upgrade/vulnerable-passwords.in
rt/lib/RT/ACL.pm
rt/lib/RT/Action/CreateTickets.pm
rt/lib/RT/Action/SendEmail.pm
rt/lib/RT/Article.pm
rt/lib/RT/Attachments.pm
rt/lib/RT/Class.pm
rt/lib/RT/Config.pm
rt/lib/RT/CustomField.pm
rt/lib/RT/Dashboard/Mailer.pm
rt/lib/RT/Date.pm
rt/lib/RT/Generated.pm
rt/lib/RT/Graph/Tickets.pm
rt/lib/RT/Group.pm
rt/lib/RT/Groups.pm
rt/lib/RT/Handle.pm
rt/lib/RT/I18N.pm
rt/lib/RT/Installer.pm
rt/lib/RT/Interface/Email.pm
rt/lib/RT/Interface/Web.pm
rt/lib/RT/Interface/Web/Handler.pm
rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm
rt/lib/RT/Lifecycle.pm
rt/lib/RT/Links.pm
rt/lib/RT/ObjectCustomField.pm
rt/lib/RT/ObjectCustomFieldValue.pm
rt/lib/RT/Queue.pm
rt/lib/RT/Reminders.pm
rt/lib/RT/Report/Tickets.pm
rt/lib/RT/Report/Tickets/Entry.pm
rt/lib/RT/Scrip.pm
rt/lib/RT/SearchBuilder.pm
rt/lib/RT/Shredder.pm
rt/lib/RT/Shredder/Plugin.pm
rt/lib/RT/Shredder/Queue.pm
rt/lib/RT/Template.pm
rt/lib/RT/Test.pm
rt/lib/RT/Test/Web.pm
rt/lib/RT/Ticket.pm
rt/lib/RT/Tickets.pm
rt/lib/RT/Transaction.pm
rt/lib/RT/URI.pm
rt/lib/RT/URI/fsck_com_article.pm
rt/lib/RT/User.pm
rt/lib/RT/Users.pm
rt/sbin/rt-server.fcgi.in
rt/sbin/rt-server.in
rt/sbin/rt-shredder.in
rt/sbin/rt-test-dependencies.in
rt/sbin/standalone_httpd
rt/sbin/standalone_httpd.in
rt/share/html/Admin/Articles/Elements/Topics
rt/share/html/Admin/CustomFields/Modify.html
rt/share/html/Admin/Elements/EditCustomFields
rt/share/html/Admin/Elements/EditRights
rt/share/html/Admin/Elements/Portal
rt/share/html/Admin/Elements/SelectNewGroupMembers
rt/share/html/Admin/Groups/index.html
rt/share/html/Admin/Tools/Queries.html
rt/share/html/Admin/Tools/Shredder/Dumps/dhandler
rt/share/html/Admin/Tools/Shredder/Elements/Error/NoStorage
rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Attachment
rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--Ticket
rt/share/html/Admin/Tools/Shredder/Elements/Object/RT--User
rt/share/html/Admin/Users/Modify.html
rt/share/html/Admin/Users/index.html
rt/share/html/Approvals/Elements/PendingMyApproval
rt/share/html/Articles/Article/Edit.html
rt/share/html/Articles/Article/Elements/EditTopics
rt/share/html/Articles/Article/ExtractIntoClass.html
rt/share/html/Articles/Topics.html
rt/share/html/Elements/CollectionAsTable/Header
rt/share/html/Elements/CollectionList
rt/share/html/Elements/CollectionListPaging
rt/share/html/Elements/ColumnMap
rt/share/html/Elements/CreateTicket
rt/share/html/Elements/EditCustomField
rt/share/html/Elements/EditCustomFieldAutocomplete
rt/share/html/Elements/EditCustomFieldSelect
rt/share/html/Elements/Error
rt/share/html/Elements/Header
rt/share/html/Elements/HeaderJavascript
rt/share/html/Elements/MessageBox
rt/share/html/Elements/RT__CustomField/ColumnMap
rt/share/html/Elements/RT__Dashboard/ColumnMap
rt/share/html/Elements/RT__Queue/ColumnMap
rt/share/html/Elements/SelectOwner
rt/share/html/Elements/SelectOwnerAutocomplete
rt/share/html/Elements/SelectStatus
rt/share/html/Elements/ShowCustomFields
rt/share/html/Elements/ShowLink
rt/share/html/Elements/ShowSearch
rt/share/html/Elements/ShowUser
rt/share/html/Elements/Submit
rt/share/html/Elements/Tabs
rt/share/html/Helpers/Autocomplete/CustomFieldValues
rt/share/html/Helpers/Toggle/ShowRequestor
rt/share/html/Install/DatabaseType.html
rt/share/html/Install/Finish.html
rt/share/html/Install/Initialize.html
rt/share/html/Install/index.html
rt/share/html/NoAuth/Logout.html
rt/share/html/NoAuth/css/aileron/InHeader
rt/share/html/NoAuth/css/aileron/boxes.css
rt/share/html/NoAuth/css/aileron/msie-pie.css [deleted file]
rt/share/html/NoAuth/css/images/PIE.htc [deleted file]
rt/share/html/NoAuth/css/web2/InHeader
rt/share/html/NoAuth/css/web2/msie-pie.css [deleted file]
rt/share/html/NoAuth/js/titlebox-state.js
rt/share/html/NoAuth/js/userautocomplete.js
rt/share/html/NoAuth/js/util.js
rt/share/html/Prefs/Search.html
rt/share/html/REST/1.0/Forms/ticket/default
rt/share/html/REST/1.0/Forms/ticket/links
rt/share/html/REST/1.0/Forms/transaction/default
rt/share/html/REST/1.0/ticket/link
rt/share/html/Search/Build.html
rt/share/html/Search/Chart.html
rt/share/html/Search/Elements/BuildFormatString
rt/share/html/Search/Elements/Chart
rt/share/html/Search/Elements/PickBasics
rt/share/html/Search/Elements/PickCFs
rt/share/html/Search/Elements/PickCriteria
rt/share/html/Search/Results.html
rt/share/html/Search/Simple.html
rt/share/html/SelfService/Elements/MyRequests
rt/share/html/SelfService/index.html
rt/share/html/Ticket/Create.html
rt/share/html/Ticket/Display.html
rt/share/html/Ticket/Elements/Bookmark
rt/share/html/Ticket/Elements/ClickToShowHistory
rt/share/html/Ticket/Elements/FoldStanzaJS
rt/share/html/Ticket/Elements/Reminders
rt/share/html/Ticket/Elements/ShowHistory
rt/share/html/Ticket/Elements/ShowRequestor
rt/share/html/Ticket/Elements/UpdateCc
rt/share/html/Ticket/GnuPG.html
rt/share/html/Ticket/Graphs/Elements/EditGraphProperties
rt/share/html/Ticket/Graphs/Elements/ShowGraph
rt/share/html/Ticket/Graphs/dhandler
rt/share/html/Ticket/ModifyLinks.html
rt/share/html/Widgets/ComboBox
rt/share/html/Widgets/TitleBoxStart
rt/share/html/index.html
rt/share/html/l
rt/share/html/m/_elements/footer
rt/share/html/m/ticket/create
rt/share/html/m/ticket/show
rt/share/html/m/tickets/search
rt/t/api/date.t
rt/t/api/tickets.t
rt/t/lifecycles/basics.t
rt/t/mail/mime_decoding.t
rt/t/web/case-sensitivity.t
rt/t/web/query_builder.t
rt/t/web/redirect-after-login.t
rt/t/web/rest.t
rt/t/web/scrub.t
rt/t/web/ticket_forward.t
rt/t/web/ticket_links.t

index 4556b0e..f6ad714 100644 (file)
@@ -580,7 +580,7 @@ sub mason_interps {
                     },
     compiler     => HTML::Mason::Compiler::ToObject->new(
                       default_escape_flags => 'h',
                     },
     compiler     => HTML::Mason::Compiler::ToObject->new(
                       default_escape_flags => 'h',
-                      allow_globals        => [qw(%session)],
+                      allow_globals        => [qw(%session $DECODED_ARGS)],
                     ),
   );
 
                     ),
   );
 
index 98c2c30..b415a06 100644 (file)
@@ -355,7 +355,7 @@ test:
 parallel-test: test-parallel
 
 test-parallel: 
 parallel-test: test-parallel
 
 test-parallel: 
-       RT_TEST_PARALLEL=1 $(PERL) "-MApp::Prove" -e 'my $$p = App::Prove->new(); $$p->process_args("-wlrj5","--state=slow,save", "t"); $$p->run()'
+       RT_TEST_PARALLEL=1 $(PERL) "-MApp::Prove" -e 'my $$p = App::Prove->new(); $$p->process_args("-wlrj5","--state=slow,save", "t"); exit( $$p->run() ? 0 : 1 )'
 
 regression-reset-db: force-dropdb
        $(PERL) -I$(LOCAL_LIB_PATH) -I$(RT_LIB_PATH) sbin/rt-setup-database --action init --dba-password ''
 
 regression-reset-db: force-dropdb
        $(PERL) -I$(LOCAL_LIB_PATH) -I$(RT_LIB_PATH) sbin/rt-setup-database --action init --dba-password ''
index 01e4a19..32f459a 100755 (executable)
--- a/rt/bin/rt
+++ b/rt/bin/rt
@@ -905,11 +905,6 @@ sub link {
     
     if (@ARGV == 3) {
         my ($from, $rel, $to) = @ARGV;
     
     if (@ARGV == 3) {
         my ($from, $rel, $to) = @ARGV;
-        if ($from !~ /^\d+$/ || $to !~ /^\d+$/) {
-            my $bad = $from =~ /^\d+$/ ? $to : $from;
-            whine "Invalid $type ID '$bad' specified.";
-            $bad = 1;
-        }
         if (($type eq "ticket") && ( ! exists $ltypes{lc $rel})) {
             whine "Invalid link '$rel' for type $type specified.";
             $bad = 1;
         if (($type eq "ticket") && ( ! exists $ltypes{lc $rel})) {
             whine "Invalid link '$rel' for type $type specified.";
             $bad = 1;
@@ -1710,7 +1705,7 @@ sub prettyshow {
         }
         print "$k->{Content}\n" if exists $k->{Content} and
                                    $k->{Content} !~ /to have no content$/ and
         }
         print "$k->{Content}\n" if exists $k->{Content} and
                                    $k->{Content} !~ /to have no content$/ and
-                                   $k->{Type} ne 'EmailRecord';
+                                   ($k->{Type}||'') ne 'EmailRecord';
         print "$k->{Attachments}\n" if exists $k->{Attachments} and
                                    $k->{Attachments};
     }
         print "$k->{Attachments}\n" if exists $k->{Attachments} and
                                    $k->{Attachments};
     }
index b125a94..72cada6 100644 (file)
@@ -172,7 +172,6 @@ sub setup_session {
     my $self = shift;
     my $opts = shift;
     my %post_params;
     my $self = shift;
     my $opts = shift;
     my %post_params;
-    $post_params{SessionType} = 'REST';    # Surpress login box
     foreach (qw(queue action)) {
         $post_params{$_} = $opts->{$_} if defined $opts->{$_};
     }
     foreach (qw(queue action)) {
         $post_params{$_} = $opts->{$_} if defined $opts->{$_};
     }
@@ -253,20 +252,13 @@ sub check_failure {
     my $r    = shift;
     return if $r->is_success;
 
     my $r    = shift;
     return if $r->is_success;
 
-    # This ordinarily oughtn't to be able to happen, suggests a bug in RT.
-    # So only load these heavy modules when they're needed.
-    require HTML::TreeBuilder;
-    require HTML::FormatText;
+    # XXX TODO 4.2: Remove the multi-line error strings in favor of something more concise
+    print STDERR <<"    ERROR";
+An Error Occurred
+=================
 
 
-    my $error = $r->error_as_HTML;
-    my $tree  = HTML::TreeBuilder->new->parse($error);
-    $tree->eof;
-
-    # It'll be a cold day in hell before RT sends out bounces in HTML
-    my $formatter =
-        HTML::FormatText->new( leftmargin  => 0,
-                               rightmargin => 50, );
-    print STDERR $formatter->format($tree);
+@{[ $r->status_line ]}
+    ERROR
     print STDERR "\n$0: undefined server error\n" if $opts->{'debug'};
     return $self->tempfail();
 }
     print STDERR "\n$0: undefined server error\n" if $opts->{'debug'};
     return $self->tempfail();
 }
index 5e1c053..e54a07a 100644 (file)
@@ -905,11 +905,6 @@ sub link {
     
     if (@ARGV == 3) {
         my ($from, $rel, $to) = @ARGV;
     
     if (@ARGV == 3) {
         my ($from, $rel, $to) = @ARGV;
-        if ($from !~ /^\d+$/ || $to !~ /^\d+$/) {
-            my $bad = $from =~ /^\d+$/ ? $to : $from;
-            whine "Invalid $type ID '$bad' specified.";
-            $bad = 1;
-        }
         if (($type eq "ticket") && ( ! exists $ltypes{lc $rel})) {
             whine "Invalid link '$rel' for type $type specified.";
             $bad = 1;
         if (($type eq "ticket") && ( ! exists $ltypes{lc $rel})) {
             whine "Invalid link '$rel' for type $type specified.";
             $bad = 1;
@@ -1710,7 +1705,7 @@ sub prettyshow {
         }
         print "$k->{Content}\n" if exists $k->{Content} and
                                    $k->{Content} !~ /to have no content$/ and
         }
         print "$k->{Content}\n" if exists $k->{Content} and
                                    $k->{Content} !~ /to have no content$/ and
-                                   $k->{Type} ne 'EmailRecord';
+                                   ($k->{Type}||'') ne 'EmailRecord';
         print "$k->{Attachments}\n" if exists $k->{Attachments} and
                                    $k->{Attachments};
     }
         print "$k->{Attachments}\n" if exists $k->{Attachments} and
                                    $k->{Attachments};
     }
index 4decdbd..1862c5f 100755 (executable)
@@ -1,7 +1,7 @@
 #! /bin/sh
 # From configure.ac Revision.
 # Guess values for system-dependent variables and create Makefiles.
 #! /bin/sh
 # From configure.ac Revision.
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for RT rt-4.0.5.
+# Generated by GNU Autoconf 2.67 for RT rt-4.0.6.
 #
 # Report bugs to <rt-bugs@bestpractical.com>.
 #
 #
 # Report bugs to <rt-bugs@bestpractical.com>.
 #
@@ -92,7 +92,6 @@ fi
 IFS=" ""       $as_nl"
 
 # Find who we are.  Look in the path if we contain no directory separator.
 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
 case $0 in #((
   *[\\/]* ) as_myself=$0 ;;
   *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -217,18 +216,11 @@ 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.
   # 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
        BASH_ENV=/dev/null
        ENV=/dev/null
        (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
        export CONFIG_SHELL
-       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+"$@"}
+       exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
 fi
 
     if test x$as_have_required = xno; then :
 fi
 
     if test x$as_have_required = xno; then :
@@ -560,8 +552,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='RT'
 PACKAGE_TARNAME='rt'
 # Identity of this package.
 PACKAGE_NAME='RT'
 PACKAGE_TARNAME='rt'
-PACKAGE_VERSION='rt-4.0.5'
-PACKAGE_STRING='RT rt-4.0.5'
+PACKAGE_VERSION='rt-4.0.6'
+PACKAGE_STRING='RT rt-4.0.6'
 PACKAGE_BUGREPORT='rt-bugs@bestpractical.com'
 PACKAGE_URL=''
 
 PACKAGE_BUGREPORT='rt-bugs@bestpractical.com'
 PACKAGE_URL=''
 
@@ -1173,7 +1165,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
     $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
     ;;
 
   esac
@@ -1311,7 +1303,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
   # 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.5 to adapt to many kinds of systems.
+\`configure' configures RT rt-4.0.6 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1372,7 +1364,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of RT rt-4.0.5:";;
+     short | recursive ) echo "Configuration of RT rt-4.0.6:";;
    esac
   cat <<\_ACEOF
 
    esac
   cat <<\_ACEOF
 
@@ -1496,8 +1488,8 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-RT configure rt-4.0.5
-generated by GNU Autoconf 2.68
+RT configure rt-4.0.6
+generated by GNU Autoconf 2.67
 
 Copyright (C) 2010 Free Software Foundation, Inc.
 This configure script is free software; the Free Software Foundation
 
 Copyright (C) 2010 Free Software Foundation, Inc.
 This configure script is free software; the Free Software Foundation
@@ -1543,7 +1535,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
 
        ac_retval=1
 fi
 
        ac_retval=1
 fi
-  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
   as_fn_set_status $ac_retval
 
 } # ac_fn_c_try_compile
   as_fn_set_status $ac_retval
 
 } # ac_fn_c_try_compile
@@ -1589,7 +1581,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
   # 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; ${as_lineno_stack:+:} unset as_lineno
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
   as_fn_set_status $ac_retval
 
 } # ac_fn_c_try_link
   as_fn_set_status $ac_retval
 
 } # ac_fn_c_try_link
@@ -1597,8 +1589,8 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
 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.5, which was
-generated by GNU Autoconf 2.68.  Invocation command line was
+It was created by RT $as_me rt-4.0.6, which was
+generated by GNU Autoconf 2.67.  Invocation command line was
 
   $ $0 $@
 
 
   $ $0 $@
 
@@ -1856,7 +1848,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
       || { { $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
 
   fi
 done
 
@@ -1954,7 +1946,7 @@ rt_version_major=4
 
 rt_version_minor=0
 
 
 rt_version_minor=0
 
-rt_version_patch=5
+rt_version_patch=6
 
 test "x$rt_version_major" = 'x' && rt_version_major=0
 test "x$rt_version_minor" = 'x' && rt_version_minor=0
 
 test "x$rt_version_major" = 'x' && rt_version_major=0
 test "x$rt_version_minor" = 'x' && rt_version_minor=0
@@ -2006,7 +1998,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
 { $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 ${ac_cv_path_install+:} false; then :
+if test "${ac_cv_path_install+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
   $as_echo_n "(cached) " >&6
 else
   as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -2087,7 +2079,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; }
 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 ${ac_cv_path_PERL+:} false; then :
+if test "${ac_cv_path_PERL+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   case $PERL in
   $as_echo_n "(cached) " >&6
 else
   case $PERL in
@@ -2808,7 +2800,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; }
 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 ${ac_cv_prog_CC+:} false; then :
+if test "${ac_cv_prog_CC+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   if test -n "$CC"; then
   $as_echo_n "(cached) " >&6
 else
   if test -n "$CC"; then
@@ -2848,7 +2840,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; }
 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 ${ac_cv_prog_ac_ct_CC+:} false; then :
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   if test -n "$ac_ct_CC"; then
   $as_echo_n "(cached) " >&6
 else
   if test -n "$ac_ct_CC"; then
@@ -2901,7 +2893,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; }
 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 ${ac_cv_prog_CC+:} false; then :
+if test "${ac_cv_prog_CC+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   if test -n "$CC"; then
   $as_echo_n "(cached) " >&6
 else
   if test -n "$CC"; then
@@ -2941,7 +2933,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; }
 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 ${ac_cv_prog_CC+:} false; then :
+if test "${ac_cv_prog_CC+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   if test -n "$CC"; then
   $as_echo_n "(cached) " >&6
 else
   if test -n "$CC"; then
@@ -3000,7 +2992,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; }
 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 ${ac_cv_prog_CC+:} false; then :
+if test "${ac_cv_prog_CC+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   if test -n "$CC"; then
   $as_echo_n "(cached) " >&6
 else
   if test -n "$CC"; then
@@ -3044,7 +3036,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; }
 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 ${ac_cv_prog_ac_ct_CC+:} false; then :
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   if test -n "$ac_ct_CC"; then
   $as_echo_n "(cached) " >&6
 else
   if test -n "$ac_ct_CC"; then
@@ -3099,7 +3091,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
 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
 
 # Provide some information about the compiler.
 $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
@@ -3214,7 +3206,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
 { { $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; }
 else
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
@@ -3257,7 +3249,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
   { { $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
 fi
 rm -f conftest conftest$ac_cv_exeext
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
@@ -3316,7 +3308,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'.
 $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
     fi
   fi
 fi
@@ -3327,7 +3319,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; }
 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 ${ac_cv_objext+:} false; then :
+if test "${ac_cv_objext+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
   $as_echo_n "(cached) " >&6
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -3368,7 +3360,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
 { { $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
 fi
 rm -f conftest.$ac_cv_objext conftest.$ac_ext
 fi
@@ -3378,7 +3370,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; }
 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 ${ac_cv_c_compiler_gnu+:} false; then :
+if test "${ac_cv_c_compiler_gnu+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
   $as_echo_n "(cached) " >&6
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -3415,7 +3407,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; }
 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 ${ac_cv_prog_cc_g+:} false; then :
+if test "${ac_cv_prog_cc_g+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   ac_save_c_werror_flag=$ac_c_werror_flag
   $as_echo_n "(cached) " >&6
 else
   ac_save_c_werror_flag=$ac_c_werror_flag
@@ -3493,7 +3485,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; }
 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 ${ac_cv_prog_cc_c89+:} false; then :
+if test "${ac_cv_prog_cc_c89+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   ac_cv_prog_cc_c89=no
   $as_echo_n "(cached) " >&6
 else
   ac_cv_prog_cc_c89=no
@@ -3591,7 +3583,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; }
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for aginitlib in -lgraph" >&5
 $as_echo_n "checking for aginitlib in -lgraph... " >&6; }
-if ${ac_cv_lib_graph_aginitlib+:} false; then :
+if test "${ac_cv_lib_graph_aginitlib+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
@@ -3625,7 +3617,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; }
 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" = xyes; then :
+if test "x$ac_cv_lib_graph_aginitlib" = x""yes; then :
   RT_GRAPHVIZ="1"
 fi
 
   RT_GRAPHVIZ="1"
 fi
 
@@ -3651,7 +3643,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; }
 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 ${ac_cv_prog_RT_GD+:} false; then :
+if test "${ac_cv_prog_RT_GD+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   if test -n "$RT_GD"; then
   $as_echo_n "(cached) " >&6
 else
   if test -n "$RT_GD"; then
@@ -3707,7 +3699,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; }
 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 ${ac_cv_prog_RT_GPG+:} false; then :
+if test "${ac_cv_prog_RT_GPG+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   if test -n "$RT_GPG"; then
   $as_echo_n "(cached) " >&6
 else
   if test -n "$RT_GPG"; then
@@ -3992,21 +3984,10 @@ $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
      :end' >>confcache
 if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
   if test -w "$cache_file"; then
-    if test "x$cache_file" != "x/dev/null"; then
+    test "x$cache_file" != "x/dev/null" &&
       { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
 $as_echo "$as_me: updating cache $cache_file" >&6;}
       { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
 $as_echo "$as_me: updating cache $cache_file" >&6;}
-      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
+    cat confcache >$cache_file
   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;}
   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;}
@@ -4074,7 +4055,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"
 ac_write_fail=0
 ac_clean_files_save=$ac_clean_files
 ac_clean_files="$ac_clean_files $CONFIG_STATUS"
@@ -4175,7 +4156,6 @@ fi
 IFS=" ""       $as_nl"
 
 # Find who we are.  Look in the path if we contain no directory separator.
 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
 case $0 in #((
   *[\\/]* ) as_myself=$0 ;;
   *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -4482,8 +4462,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="
 # 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.5, which was
-generated by GNU Autoconf 2.68.  Invocation command line was
+This file was extended by RT $as_me rt-4.0.6, which was
+generated by GNU Autoconf 2.67.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
   CONFIG_HEADERS  = $CONFIG_HEADERS
 
   CONFIG_FILES    = $CONFIG_FILES
   CONFIG_HEADERS  = $CONFIG_HEADERS
@@ -4535,8 +4515,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="\\
 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.5
-configured by $0, generated by GNU Autoconf 2.68,
+RT config.status rt-4.0.6
+configured by $0, generated by GNU Autoconf 2.67,
   with options \\"\$ac_cs_config\\"
 
 Copyright (C) 2010 Free Software Foundation, Inc.
   with options \\"\$ac_cs_config\\"
 
 Copyright (C) 2010 Free Software Foundation, Inc.
@@ -4678,7 +4658,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" ;;
 
     "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
 
   esac
 done
 
@@ -4699,10 +4679,9 @@ fi
 # after its creation but before its name has been assigned to `$tmp'.
 $debug ||
 {
 # after its creation but before its name has been assigned to `$tmp'.
 $debug ||
 {
-  tmp= ac_tmp=
+  tmp=
   trap 'exit_status=$?
   trap 'exit_status=$?
-  : "${ac_tmp:=$tmp}"
-  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+  { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
 ' 0
   trap 'as_fn_exit 1' 1 2 13 15
 }
 ' 0
   trap 'as_fn_exit 1' 1 2 13 15
 }
@@ -4710,13 +4689,12 @@ $debug ||
 
 {
   tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
 
 {
   tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
-  test -d "$tmp"
+  test -n "$tmp" && test -d "$tmp"
 }  ||
 {
   tmp=./conf$$-$RANDOM
   (umask 077 && mkdir "$tmp")
 } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
 }  ||
 {
   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.
 
 # Set up the scripts for CONFIG_FILES section.
 # No need to generate them if there are no CONFIG_FILES.
@@ -4738,7 +4716,7 @@ else
   ac_cs_awk_cr=$ac_cr
 fi
 
   ac_cs_awk_cr=$ac_cr
 fi
 
-echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+echo 'BEGIN {' >"$tmp/subs1.awk" &&
 _ACEOF
 
 
 _ACEOF
 
 
@@ -4766,7 +4744,7 @@ done
 rm -f conf$$subs.sh
 
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 rm -f conf$$subs.sh
 
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
-cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
 _ACEOF
 sed -n '
 h
 _ACEOF
 sed -n '
 h
@@ -4814,7 +4792,7 @@ t delim
 rm -f conf$$subs.awk
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 _ACAWK
 rm -f conf$$subs.awk
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 _ACAWK
-cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+cat >>"\$tmp/subs1.awk" <<_ACAWK &&
   for (key in S) S_is_set[key] = 1
   FS = "\a"
 
   for (key in S) S_is_set[key] = 1
   FS = "\a"
 
@@ -4846,7 +4824,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
   sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
 else
   cat
-fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
   || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
 _ACEOF
 
   || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
 _ACEOF
 
@@ -4886,7 +4864,7 @@ do
   esac
   case $ac_mode$ac_tag in
   :[FHL]*:*);;
   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
   :[FH]-) ac_tag=-:-;;
   :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
   esac
@@ -4905,7 +4883,7 @@ do
     for ac_f
     do
       case $ac_f in
     for ac_f
     do
       case $ac_f in
-      -) ac_f="$ac_tmp/stdin";;
+      -) ac_f="$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 `:'.
       *) # 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 `:'.
@@ -4914,7 +4892,7 @@ do
           [\\/$]*) false;;
           *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
           esac ||
           [\\/$]*) 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'"
       esac
       case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
       as_fn_append ac_file_inputs " '$ac_f'"
@@ -4940,8 +4918,8 @@ $as_echo "$as_me: creating $ac_file" >&6;}
     esac
 
     case $ac_tag in
     esac
 
     case $ac_tag in
-    *:-:* | *:-) cat >"$ac_tmp/stdin" \
-      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    *:-:* | *:-) cat >"$tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5  ;;
     esac
     ;;
   esac
     esac
     ;;
   esac
@@ -5071,22 +5049,21 @@ s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
 s&@INSTALL@&$ac_INSTALL&;t t
 $ac_datarootdir_hack
 "
 s&@INSTALL@&$ac_INSTALL&;t t
 $ac_datarootdir_hack
 "
-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
+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
 
 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
 
 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
-  { 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"; } &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' "$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;}
 
   { $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 "$ac_tmp/stdin"
+  rm -f "$tmp/stdin"
   case $ac_file in
   case $ac_file in
-  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
-  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  -) cat "$tmp/out" && rm -f "$tmp/out";;
+  *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
   esac \
   || as_fn_error $? "could not create $ac_file" "$LINENO" 5
  ;;
   esac \
   || as_fn_error $? "could not create $ac_file" "$LINENO" 5
  ;;
index a930134..4b64d2e 100644 (file)
@@ -106,3 +106,26 @@ with
   database level.
 
 *******
   database level.
 
 *******
+
+UPGRADING FROM 4.0.5 and earlier - Changes:
+
+The fix for an attribute truncation bug on MySQL requires a small ALTER TABLE.
+Be sure you run `make upgrade-database` to apply this change automatically.
+The bug primarily manifested when uploading large logos in the theme editor on
+MySQL.  Refer to etc/upgrade/4.0.6/schema.mysql for the actual ALTER TABLE that
+will be run.
+
+*******
+The web-based query builder now uses Queue limits to restrict the set of
+displayed statuses and owners.  As part of this change, the %cfqueues
+parameter was renamed to %Queues; if you have local modifications to any
+of the following Mason templates, this feature will not function
+correctly:
+
+    share/html/Elements/SelectOwner
+    share/html/Elements/SelectStatus
+    share/html/Prefs/Search.html
+    share/html/Search/Build.html
+    share/html/Search/Elements/BuildFormatString
+    share/html/Search/Elements/PickCFs
+    share/html/Search/Elements/PickCriteria
index 8aa84fd..396c562 100644 (file)
@@ -186,11 +186,11 @@ which will be significantly faster:
 
     make test-parallel
 
 
     make test-parallel
 
-The C<*-trunk> and C<master> branches are expected to be passing always
-be passing all tests.  While it is acceptable to break tests in an
-intermediate commit, a branch which does not pass tests will not be
-merged.  Ideally, commits which fix a bug should also include a testcase
-which fails before the fix and succeeds after.
+The C<*-trunk> and C<master> branches are expected to always be passing
+all tests.  While it is acceptable to break tests in an intermediate
+commit, a branch which does not pass tests will not be merged.  Ideally,
+commits which fix a bug should also include a testcase which fails
+before the fix and succeeds after.
 
 
 
 
 
 
index b8650e0..620f868 100644 (file)
@@ -9,6 +9,21 @@ key).
 
 More information is available at L<http://bestpractical.com/security/>.
 
 
 More information is available at L<http://bestpractical.com/security/>.
 
+
+=head2 RT's security process
+
+After a security vulnerability is reported to Best Practical and
+verified, we attempt to resolve it in as timely a fashion as possible.
+Best Practical support customers will be notified before we disclose the
+information to the public.  All security announcements will be sent to
+C<rt-announce@bestpractical.com>, which includes
+C<rt-users@bestpractical.com> and C<rt-devel@bestpractical.com>.
+
+As the tests for security vulnerabilities are often nearly identical to
+working exploits, sensitive tests will be embargoed for a period of six
+months before being added to the public RT repository.
+
+
 =head2 Security tips for running RT
 
 =over
 =head2 Security tips for running RT
 
 =over
index 65065c5..4c3f73f 100644 (file)
@@ -67,6 +67,19 @@ spontaneously logged in as other users in the system.
 
 =head3 mod_fcgid
 
 
 =head3 mod_fcgid
 
+B<WARNING>: Before mod_fcgid 2.3.6, the maximum request size was 1GB.
+Starting in 2.3.6, this is now 128Kb.  This is unlikely to be large
+enough for any RT install that handles attachments.  You can read more
+about FcgidMaxRequestLen at
+L<http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html#fcgidmaxrequestlen>
+
+Most distributions will have a mod_fcgid.conf or similar file with
+mod_fcgid configurations and you should add:
+
+    FcgidMaxRequestLen 1073741824
+
+to return to the old default.
+
     <VirtualHost rt.example.com>
         ### Optional apache logs for RT
         # Ensure that your log rotation scripts know about these files
     <VirtualHost rt.example.com>
         ### Optional apache logs for RT
         # Ensure that your log rotation scripts know about these files
index 925f0ca..de7660a 100644 (file)
@@ -350,13 +350,8 @@ Set($StoreLoops, undef);
 C<$MaxAttachmentSize> sets the maximum size (in bytes) of attachments
 stored in the database.
 
 C<$MaxAttachmentSize> sets the maximum size (in bytes) of attachments
 stored in the database.
 
-For MySQL and Oracle, we set this size to 10 megabytes.  If you're
-running a PostgreSQL version earlier than 7.1, you will need to drop
-this to 8192. (8k)
-
 =cut
 
 =cut
 
-
 Set($MaxAttachmentSize, 10_000_000);
 
 =item C<$TruncateLongAttachments>
 Set($MaxAttachmentSize, 10_000_000);
 
 =item C<$TruncateLongAttachments>
@@ -892,6 +887,8 @@ Set($CanonicalizeRedirectURLs, 0);
 A list of JavaScript files to be included in head.  Removing any of
 the default entries is not suggested.
 
 A list of JavaScript files to be included in head.  Removing any of
 the default entries is not suggested.
 
+If you're a plugin author, refer to RT->AddJavaScript.
+
 =cut
 
 Set(@JSFiles, qw/
 =cut
 
 Set(@JSFiles, qw/
@@ -928,6 +925,8 @@ directory, or from http://www.crockford.com/javascript/jsmin.html
 
 A list of additional CSS files to be included in head.
 
 
 A list of additional CSS files to be included in head.
 
+If you're a plugin author, refer to RT->AddStyleSheets.
+
 =cut
 
 Set(@CSSFiles, qw//);
 =cut
 
 Set(@CSSFiles, qw//);
@@ -1789,8 +1788,50 @@ This disables RT's clickjacking protection.
 
 Set($Framebusting, 1);
 
 
 Set($Framebusting, 1);
 
+=item C<$RestrictReferrer>
+
+If set to a false value, the HTTP C<Referer> (sic) header will not be
+checked to ensure that requests come from RT's own domain.  As RT allows
+for GET requests to alter state, disabling this opens RT up to
+cross-site request forgery (CSRF) attacks.
+
+=cut
+
+Set($RestrictReferrer, 1);
+
+=item C<$RestrictLoginReferrer>
+
+If set to a false value, RT will allow the user to log in from any link
+or request, merely by passing in C<user> and C<pass> parameters; setting
+it to a true value forces all logins to come from the login box, so the
+user is aware that they are being logged in.  The default is off, for
+backwards compatability.
+
+=cut
+
+Set($RestrictLoginReferrer, 0);
+
+=item C<$ReferrerWhitelist>
+
+This is a list of hostname:port combinations that RT will treat as being
+part of RT's domain. This is particularly useful if you access RT as
+multiple hostnames or have an external auth system that needs to
+redirect back to RT once authentication is complete.
+
+ Set(@ReferrerWhitelist, qw(www.example.com:443  www3.example.com:80));
+
+If the "RT has detected a possible cross-site request forgery" error is triggered
+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.
+
+=cut
+
+Set(@ReferrerWhitelist, qw());
+
 =back
 
 =back
 
+
+
 =head1 Authorization and user configuration
 
 =over 4
 =head1 Authorization and user configuration
 
 =over 4
@@ -1856,10 +1897,9 @@ Set($AutoCreate, undef);
 
 =item C<$WebSessionClass>
 
 
 =item C<$WebSessionClass>
 
-C<$WebSessionClass> is the class you wish to use for managing
-Sessions.  It defaults to use your SQL database, but if you are using
-MySQL 3.x and plans to use non-ascii Queue names, uncomment and add
-this line to F<RT_SiteConfig.pm> to prevent session corruption.
+C<$WebSessionClass> is the class you wish to use for managing sessions.
+It defaults to use your SQL database, except on Oracle, where it
+defaults to files on disk.
 
 =cut
 
 
 =cut
 
@@ -2206,6 +2246,14 @@ be changed to this value.
 When an approval is denied, the status of depending tickets will
 be changed to this value.
 
 When an approval is denied, the status of depending tickets will
 be changed to this value.
 
+=item reminder_on_open
+
+When a reminder is opened, the status will be changed to this value.
+
+=item reminder_on_resolve
+
+When a reminder is resolved, the status will be changed to this value.
+
 =back
 
 =head2 Transitions between statuses and UI actions
 =back
 
 =head2 Transitions between statuses and UI actions
@@ -2352,6 +2400,8 @@ Set(%Lifecycles,
             on_merge  => 'resolved',
             approved  => 'open',
             denied    => 'rejected',
             on_merge  => 'resolved',
             approved  => 'open',
             denied    => 'rejected',
+            reminder_on_open     => 'open',
+            reminder_on_resolve  => 'resolved',
         },
 
         transitions => {
         },
 
         transitions => {
@@ -2425,6 +2475,8 @@ Set(%Lifecycles,
         defaults => {
             on_create => 'new',
             on_merge => 'resolved',
         defaults => {
             on_create => 'new',
             on_merge => 'resolved',
+            reminder_on_open     => 'open',
+            reminder_on_resolve  => 'resolved',
         },
 
         transitions => {
         },
 
         transitions => {
@@ -2529,7 +2581,7 @@ Set(%AdminSearchResultFormat,
     Queues =>
         q{'<a href="__WebPath__/Admin/Queues/Modify.html?id=__id__">__id__</a>/TITLE:#'}
         .q{,'<a href="__WebPath__/Admin/Queues/Modify.html?id=__id__">__Name__</a>/TITLE:Name'}
     Queues =>
         q{'<a href="__WebPath__/Admin/Queues/Modify.html?id=__id__">__id__</a>/TITLE:#'}
         .q{,'<a href="__WebPath__/Admin/Queues/Modify.html?id=__id__">__Name__</a>/TITLE:Name'}
-        .q{,__Description__,__Address__,__Priority__,__DefaultDueIn__,__Disabled__},
+        .q{,__Description__,__Address__,__Priority__,__DefaultDueIn__,'__Disabled__,__Lifecycle__},
 
     Groups =>
         q{'<a href="__WebPath__/Admin/Groups/Modify.html?id=__id__">__id__</a>/TITLE:#'}
 
     Groups =>
         q{'<a href="__WebPath__/Admin/Groups/Modify.html?id=__id__">__id__</a>/TITLE:#'}
index c313aaf..9ed0337 100644 (file)
@@ -413,7 +413,7 @@ CREATE TABLE Attributes (
   id INTEGER NOT NULL  AUTO_INCREMENT,
   Name varchar(255) NULL  ,
   Description varchar(255) NULL  ,
   id INTEGER NOT NULL  AUTO_INCREMENT,
   Name varchar(255) NULL  ,
   Description varchar(255) NULL  ,
-  Content BLOB,
+  Content LONGBLOB,
   ContentType varchar(16) CHARACTER SET ascii,
   ObjectType varchar(64) CHARACTER SET ascii,
   ObjectId integer, # foreign key to anything
   ContentType varchar(16) CHARACTER SET ascii,
   ObjectType varchar(64) CHARACTER SET ascii,
   ObjectId integer, # foreign key to anything
index 728786f..a3d719c 100755 (executable)
@@ -89,6 +89,9 @@ push @{$users->{'restrictions'}{ "main.Password" }}, "AND", {
     value => '40',
 };
 
     value => '40',
 };
 
+# we want to update passwords on disabled users
+$users->{'find_disabled_rows'} = 1;
+
 my $count = $users->Count;
 if ($count == 0) {
     print "No users with unsalted or weak cryptography found.\n";
 my $count = $users->Count;
 if ($count == 0) {
     print "No users with unsalted or weak cryptography found.\n";
index d7c9ef2..49a7f1d 100755 (executable)
@@ -182,6 +182,9 @@ sub LimitToPrincipal {
                      ALIAS2 => $cgm,
                      FIELD2 => 'GroupId'
                    );
                      ALIAS2 => $cgm,
                      FIELD2 => 'GroupId'
                    );
+        $self->Limit( ALIAS => $cgm,
+                      FIELD => 'Disabled',
+                      VALUE => 0 );
         $self->Limit( ALIAS           => $cgm,
                       FIELD           => 'MemberId',
                       OPERATOR        => '=',
         $self->Limit( ALIAS           => $cgm,
                       FIELD           => 'MemberId',
                       OPERATOR        => '=',
index c26e2eb..31489c8 100644 (file)
@@ -325,9 +325,19 @@ sub Prepare {
 
     }
 
 
     }
 
+    my $active = 0;
+    if ( $self->TemplateObj->Type eq 'Perl' ) {
+        $active = 1;
+    } else {
+        RT->Logger->info(sprintf(
+            "Template #%d is type %s.  You most likely want to use a Perl template instead.",
+            $self->TemplateObj->id, $self->TemplateObj->Type
+        ));
+    }
+
     $self->Parse(
         Content        => $self->TemplateObj->Content,
     $self->Parse(
         Content        => $self->TemplateObj->Content,
-        _ActiveContent => 1
+        _ActiveContent => $active,
     );
     return 1;
 
     );
     return 1;
 
@@ -1171,6 +1181,7 @@ sub UpdateCustomFields {
         my $cf = $1;
 
         my $CustomFieldObj = RT::CustomField->new($self->CurrentUser);
         my $cf = $1;
 
         my $CustomFieldObj = RT::CustomField->new($self->CurrentUser);
+        $CustomFieldObj->SetContextObject( $ticket );
         $CustomFieldObj->LoadById($cf);
 
         my @values;
         $CustomFieldObj->LoadById($cf);
 
         my @values;
index e2aa00b..4ae1a8b 100755 (executable)
@@ -348,7 +348,7 @@ sub AddAttachments {
 
     $MIMEObj->head->delete('RT-Attach-Message');
 
 
     $MIMEObj->head->delete('RT-Attach-Message');
 
-    my $attachments = RT::Attachments->new(RT->SystemUser);
+    my $attachments = RT::Attachments->new( RT->SystemUser );
     $attachments->Limit(
         FIELD => 'TransactionId',
         VALUE => $self->TransactionObj->Id
     $attachments->Limit(
         FIELD => 'TransactionId',
         VALUE => $self->TransactionObj->Id
@@ -408,6 +408,10 @@ sub AddAttachment {
     my $attach  = shift;
     my $MIMEObj = shift || $self->TemplateObj->MIMEObj;
 
     my $attach  = shift;
     my $MIMEObj = shift || $self->TemplateObj->MIMEObj;
 
+    # $attach->TransactionObj may not always be $self->TransactionObj
+    return unless $attach->Id
+              and $attach->TransactionObj->CurrentUserCanSee;
+
     # ->attach expects just the disposition type; extract it if we have the header
     my $disp = ($attach->GetHeader('Content-Disposition') || '')
                     =~ /^\s*(inline|attachment)/i ? $1 : undef;
     # ->attach expects just the disposition type; extract it if we have the header
     my $disp = ($attach->GetHeader('Content-Disposition') || '')
                     =~ /^\s*(inline|attachment)/i ? $1 : undef;
@@ -471,8 +475,7 @@ sub AddTicket {
     my $self = shift;
     my $tid  = shift;
 
     my $self = shift;
     my $tid  = shift;
 
-    # XXX: we need a current user here, but who is current user?
-    my $attachs   = RT::Attachments->new(RT->SystemUser);
+    my $attachs   = RT::Attachments->new( $self->TransactionObj->CreatorObj );
     my $txn_alias = $attachs->TransactionAlias;
     $attachs->Limit( ALIAS => $txn_alias, FIELD => 'Type', VALUE => 'Create' );
     $attachs->Limit(
     my $txn_alias = $attachs->TransactionAlias;
     $attachs->Limit( ALIAS => $txn_alias, FIELD => 'Type', VALUE => 'Create' );
     $attachs->Limit(
index 7310241..24b952a 100644 (file)
@@ -543,6 +543,17 @@ sub CurrentUserHasRight {
 
 }
 
 
 }
 
+=head2 CurrentUserCanSee
+
+Returns true if the current user can see the article, using ShowArticle
+
+=cut
+
+sub CurrentUserCanSee {
+    my $self = shift;
+    return $self->CurrentUserHasRight('ShowArticle');
+}
+
 # }}}
 
 # {{{ _Set
 # }}}
 
 # {{{ _Set
index c640b20..2bdbc24 100755 (executable)
@@ -227,15 +227,12 @@ sub Next {
     my $Attachment = $self->SUPER::Next;
     return $Attachment unless $Attachment;
 
     my $Attachment = $self->SUPER::Next;
     return $Attachment unless $Attachment;
 
-    my $txn = $Attachment->TransactionObj;
-    if ( $txn->__Value('Type') eq 'Comment' ) {
-        return $Attachment if $txn->CurrentUserHasRight('ShowTicketComments');
-    } elsif ( $txn->CurrentUserHasRight('ShowTicket') ) {
+    if ( $Attachment->TransactionObj->CurrentUserCanSee ) {
         return $Attachment;
         return $Attachment;
+    } else {
+        # If the user doesn't have the right to show this ticket
+        return $self->Next;
     }
     }
-
-    # If the user doesn't have the right to show this ticket
-    return $self->Next;
 }
 
 
 }
 
 
index bb694ce..3906b9f 100644 (file)
@@ -275,6 +275,7 @@ sub ArticleCustomFields {
 
     my $cfs = RT::CustomFields->new( $self->CurrentUser );
     if ( $self->CurrentUserHasRight('SeeClass') ) {
 
     my $cfs = RT::CustomFields->new( $self->CurrentUser );
     if ( $self->CurrentUserHasRight('SeeClass') ) {
+        $cfs->SetContextObject( $self );
         $cfs->LimitToGlobalOrObjectId( $self->Id );
         $cfs->LimitToLookupType( RT::Article->CustomFieldLookupType );
         $cfs->ApplySortOrder;
         $cfs->LimitToGlobalOrObjectId( $self->Id );
         $cfs->LimitToLookupType( RT::Article->CustomFieldLookupType );
         $cfs->ApplySortOrder;
index cc47df3..c56d4c6 100644 (file)
@@ -620,6 +620,7 @@ our %META = (
             }
         }
     },
             }
         }
     },
+    ReferrerWhitelist => { Type => 'ARRAY' },
     ResolveDefaultUpdateType => {
         PostLoadCheck => sub {
             my $self  = shift;
     ResolveDefaultUpdateType => {
         PostLoadCheck => sub {
             my $self  = shift;
index 095caa5..263bde8 100644 (file)
@@ -474,10 +474,12 @@ sub LoadByName {
     }
 
     # if we're looking for a queue by name, make it a number
     }
 
     # if we're looking for a queue by name, make it a number
-    if ( defined $args{'Queue'} && $args{'Queue'} =~ /\D/ ) {
+    if ( defined $args{'Queue'} && ($args{'Queue'} =~ /\D/ || !$self->ContextObject) ) {
         my $QueueObj = RT::Queue->new( $self->CurrentUser );
         $QueueObj->Load( $args{'Queue'} );
         $args{'Queue'} = $QueueObj->Id;
         my $QueueObj = RT::Queue->new( $self->CurrentUser );
         $QueueObj->Load( $args{'Queue'} );
         $args{'Queue'} = $QueueObj->Id;
+        $self->SetContextObject( $QueueObj )
+            unless $self->ContextObject;
     }
 
     # XXX - really naive implementation.  Slow. - not really. still just one query
     }
 
     # XXX - really naive implementation.  Slow. - not really. still just one query
@@ -535,6 +537,8 @@ sub Values {
     # if the user has no rights, return an empty object
     if ( $self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
         $cf_values->LimitToCustomField( $self->Id );
     # if the user has no rights, return an empty object
     if ( $self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
         $cf_values->LimitToCustomField( $self->Id );
+    } else {
+        $cf_values->Limit( FIELD => 'id', VALUE => 0, SUBCLAUSE => 'acl' );
     }
     return ($cf_values);
 }
     }
     return ($cf_values);
 }
@@ -890,7 +894,77 @@ sub ContextObject {
     my $self = shift;
     return $self->{'context_object'};
 }
     my $self = shift;
     return $self->{'context_object'};
 }
-  
+
+sub ValidContextType {
+    my $self = shift;
+    my $class = shift;
+
+    my %valid;
+    $valid{$_}++ for split '-', $self->LookupType;
+    delete $valid{'RT::Transaction'};
+
+    return $valid{$class};
+}
+
+=head2 LoadContextObject
+
+Takes an Id for a Context Object and loads the right kind of RT::Object
+for this particular Custom Field (based on the LookupType) and returns it.
+This is a good way to ensure you don't try to use a Queue as a Context
+Object on a User Custom Field.
+
+=cut
+
+sub LoadContextObject {
+    my $self = shift;
+    my $type = shift;
+    my $contextid = shift;
+
+    unless ( $self->ValidContextType($type) ) {
+        RT->Logger->debug("Invalid ContextType $type for Custom Field ".$self->Id);
+        return;
+    }
+
+    my $context_object = $type->new( $self->CurrentUser );
+    my ($id, $msg) = $context_object->LoadById( $contextid );
+    unless ( $id ) {
+        RT->Logger->debug("Invalid ContextObject id: $msg");
+        return;
+    }
+    return $context_object;
+}
+
+=head2 ValidateContextObject
+
+Ensure that a given ContextObject applies to this Custom Field.
+For custom fields that are assigned to Queues or to Classes, this checks that the Custom
+Field is actually applied to that objects.  For Global Custom Fields, it returns true
+as long as the Object is of the right type, because you may be using
+your permissions on a given Queue of Class to see a Global CF.
+For CFs that are only applied Globally, you don't need a ContextObject.
+
+=cut
+
+sub ValidateContextObject {
+    my $self = shift;
+    my $object = shift;
+
+    return 1 if $self->IsApplied(0);
+
+    # global only custom fields don't have objects
+    # that should be used as context objects.
+    return if $self->ApplyGlobally;
+
+    # Otherwise, make sure we weren't passed a user object that we're
+    # supposed to treat as a queue.
+    return unless $self->ValidContextType(ref $object);
+
+    # Check that it is applied correctly
+    my ($applied_to) = grep {ref($_) eq $self->RecordClassFromLookupType} ($object, $object->ACLEquivalenceObjects);
+    return unless $applied_to;
+    return $self->IsApplied($applied_to->id);
+}
+
 
 sub _Set {
     my $self = shift;
 
 sub _Set {
     my $self = shift;
@@ -1702,6 +1776,7 @@ sub SetBasedOn {
         unless defined $value and length $value;
 
     my $cf = RT::CustomField->new( $self->CurrentUser );
         unless defined $value and length $value;
 
     my $cf = RT::CustomField->new( $self->CurrentUser );
+    $cf->SetContextObject( $self->ContextObject );
     $cf->Load( ref $value ? $value->id : $value );
 
     return (0, "Permission denied")
     $cf->Load( ref $value ? $value->id : $value );
 
     return (0, "Permission denied")
@@ -1719,6 +1794,7 @@ sub BasedOnObj {
     my $self = shift;
 
     my $obj = RT::CustomField->new( $self->CurrentUser );
     my $self = shift;
 
     my $obj = RT::CustomField->new( $self->CurrentUser );
+    $obj->SetContextObject( $self->ContextObject );
     if ( $self->BasedOn ) {
         $obj->Load( $self->BasedOn );
     }
     if ( $self->BasedOn ) {
         $obj->Load( $self->BasedOn );
     }
index 8558978..40b53b1 100644 (file)
@@ -252,7 +252,7 @@ SUMMARY
 
     $content = HTML::RewriteAttributes::Links->rewrite(
         $content,
 
     $content = HTML::RewriteAttributes::Links->rewrite(
         $content,
-        RT->Config->Get('WebURL') . '/Dashboards/Render.html',
+        RT->Config->Get('WebURL') . 'Dashboards/Render.html',
     );
 
     $self->EmailDashboard(
     );
 
     $self->EmailDashboard(
@@ -447,6 +447,9 @@ sub BuildEmail {
                 autohandler_name => '', # disable forced login and more
                 data_dir => $data_dir,
             );
                 autohandler_name => '', # disable forced login and more
                 data_dir => $data_dir,
             );
+            $mason->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
+            $mason->set_escape( u => \&RT::Interface::Web::EscapeURI  );
+            $mason->set_escape( j => \&RT::Interface::Web::EscapeJS   );
         }
         return $mason;
     }
         }
         return $mason;
     }
index 7a97fbe..442c770 100644 (file)
@@ -604,6 +604,10 @@ sub Get
     my $self = shift;
     my %args = (Format => 'ISO', @_);
     my $formatter = $args{'Format'};
     my $self = shift;
     my %args = (Format => 'ISO', @_);
     my $formatter = $args{'Format'};
+    unless ( $self->ValidFormatter($formatter) ) {
+        RT->Logger->warning("Invalid date formatter '$formatter', falling back to ISO");
+        $formatter = 'ISO';
+    }
     $formatter = 'ISO' unless $self->can($formatter);
     return $self->$formatter( %args );
 }
     $formatter = 'ISO' unless $self->can($formatter);
     return $self->$formatter( %args );
 }
@@ -642,6 +646,20 @@ sub Formatters
     return @FORMATTERS;
 }
 
     return @FORMATTERS;
 }
 
+=head3 ValidFormatter FORMAT
+
+Returns a true value if C<FORMAT> is a known formatter.  Otherwise returns
+false.
+
+=cut
+
+sub ValidFormatter {
+    my $self   = shift;
+    my $format = shift;
+    return (grep { $_ eq $format } $self->Formatters and $self->can($format))
+                ? 1 : 0;
+}
+
 =head3 DefaultFormat
 
 =cut
 =head3 DefaultFormat
 
 =cut
@@ -720,15 +738,19 @@ sub LocalizedDateTime
     my %args = ( Date => 1,
                  Time => 1,
                  Timezone => '',
     my %args = ( Date => 1,
                  Time => 1,
                  Timezone => '',
-                 DateFormat => 'date_format_full',
-                 TimeFormat => 'time_format_medium',
+                 DateFormat => '',
+                 TimeFormat => '',
                  AbbrDay => 1,
                  AbbrMonth => 1,
                  @_,
                );
 
                  AbbrDay => 1,
                  AbbrMonth => 1,
                  @_,
                );
 
-    my $date_format = $args{'DateFormat'};
-    my $time_format = $args{'TimeFormat'};
+    # Require valid names for the format methods
+    my $date_format = $args{DateFormat} =~ /^\w+$/
+                    ? $args{DateFormat} : 'date_format_full';
+
+    my $time_format = $args{TimeFormat} =~ /^\w+$/
+                    ? $args{TimeFormat} : 'time_format_medium';
 
     my $formatter = $self->LocaleObj;
     $date_format = $formatter->$date_format;
 
     my $formatter = $self->LocaleObj;
     $date_format = $formatter->$date_format;
index b02a413..2abcf3b 100644 (file)
@@ -50,7 +50,7 @@ package RT;
 use warnings;
 use strict;
 
 use warnings;
 use strict;
 
-our $VERSION = '4.0.5';
+our $VERSION = '4.0.6';
 
 
 
 
 
 
index 112934e..b839824 100644 (file)
@@ -100,7 +100,7 @@ EOT
 
 sub gv_escape($) {
     my $value = shift;
 
 sub gv_escape($) {
     my $value = shift;
-    $value =~ s{(?=")}{\\}g;
+    $value =~ s{(?=["\\])}{\\}g;
     return $value;
 }
 
     return $value;
 }
 
@@ -278,6 +278,14 @@ sub TicketLinks {
         ShowLinkDescriptions => 0,
         @_
     );
         ShowLinkDescriptions => 0,
         @_
     );
+
+    my %valid_links = map { $_ => 1 }
+        qw(Members MemberOf RefersTo ReferredToBy DependsOn DependedOnBy);
+
+    # Validate our link types
+    $args{ShowLinks}   = [ grep { $valid_links{$_} } @{$args{ShowLinks}} ];
+    $args{LeadingLink} = 'Members' unless $valid_links{ $args{LeadingLink} };
+
     unless ( $args{'Graph'} ) {
         $args{'Graph'} = GraphViz->new(
             name    => 'ticket_links_'. $args{'Ticket'}->id,
     unless ( $args{'Graph'} ) {
         $args{'Graph'} = GraphViz->new(
             name    => 'ticket_links_'. $args{'Ticket'}->id,
index 779c026..b367b2f 100755 (executable)
@@ -1171,8 +1171,18 @@ sub CurrentUserHasRight {
 }
 
 
 }
 
 
+=head2 CurrentUserCanSee
 
 
+Always returns 1; unfortunately, for historical reasons, users have
+always been able to examine groups they have indirect access to, even if
+they do not have SeeGroup explicitly.
 
 
+=cut
+
+sub CurrentUserCanSee {
+    my $self = shift;
+    return 1;
+}
 
 
 =head2 PrincipalObj
 
 
 =head2 PrincipalObj
index 46f1c23..578109c 100755 (executable)
@@ -234,6 +234,8 @@ sub WithMember {
                 ALIAS2 => $members, FIELD2 => 'GroupId');
 
     $self->Limit(ALIAS => $members, FIELD => 'MemberId', OPERATOR => '=', VALUE => $args{'PrincipalId'});
                 ALIAS2 => $members, FIELD2 => 'GroupId');
 
     $self->Limit(ALIAS => $members, FIELD => 'MemberId', OPERATOR => '=', VALUE => $args{'PrincipalId'});
+    $self->Limit(ALIAS => $members, FIELD => 'Disabled', VALUE => 0)
+        if $args{'Recursively'};
 
     return $members;
 }
 
     return $members;
 }
@@ -261,6 +263,12 @@ sub WithoutMember {
         VALUE    => $args{'PrincipalId'},
     );
     $self->Limit(
         VALUE    => $args{'PrincipalId'},
     );
     $self->Limit(
+        LEFTJOIN => $members_alias,
+        ALIAS    => $members_alias,
+        FIELD    => 'Disabled',
+        VALUE    => 0
+    ) if $args{'Recursively'};
+    $self->Limit(
         ALIAS    => $members_alias,
         FIELD    => 'MemberId',
         OPERATOR => 'IS',
         ALIAS    => $members_alias,
         FIELD    => 'MemberId',
         OPERATOR => 'IS',
index bb19aa9..99d10e3 100644 (file)
@@ -226,14 +226,12 @@ sub CheckIntegrity {
     my $self = shift;
     $self = new $self unless ref $self;
 
     my $self = shift;
     $self = new $self unless ref $self;
 
-    do {
+    unless ($RT::Handle and $RT::Handle->dbh) {
         local $@;
         unless ( eval { RT::ConnectToDatabase(); 1 } ) {
             return (0, 'no connection', "$@");
         }
         local $@;
         unless ( eval { RT::ConnectToDatabase(); 1 } ) {
             return (0, 'no connection', "$@");
         }
-    };
-
-    RT::InitLogging();
+    }
 
     require RT::CurrentUser;
     my $test_user = RT::CurrentUser->new;
 
     require RT::CurrentUser;
     my $test_user = RT::CurrentUser->new;
@@ -748,6 +746,10 @@ sub InsertData {
     my $self     = shift;
     my $datafile = shift;
     my $root_password = shift;
     my $self     = shift;
     my $datafile = shift;
     my $root_password = shift;
+    my %args     = (
+        disconnect_after => 1,
+        @_
+    );
 
     # Slurp in stuff to insert from the datafile. Possible things to go in here:-
     our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
 
     # Slurp in stuff to insert from the datafile. Possible things to go in here:-
     our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
@@ -1071,8 +1073,14 @@ sub InsertData {
         $RT::Logger->debug("done.");
     }
 
         $RT::Logger->debug("done.");
     }
 
-    my $db_type = RT->Config->Get('DatabaseType');
-    $RT::Handle->Disconnect() unless $db_type eq 'SQLite';
+    # XXX: This disconnect doesn't really belong here; it's a relict from when
+    # this method was extracted from rt-setup-database.  However, too much
+    # depends on it to change without significant testing.  At the very least,
+    # we can provide a way to skip the side-effect.
+    if ( $args{disconnect_after} ) {
+        my $db_type = RT->Config->Get('DatabaseType');
+        $RT::Handle->Disconnect() unless $db_type eq 'SQLite';
+    }
 
     $RT::Logger->debug("Done setting up database content.");
 
 
     $RT::Logger->debug("Done setting up database content.");
 
index 971eaa1..cadf7cc 100644 (file)
@@ -219,13 +219,6 @@ sub SetMIMEEntityToEncoding {
 
     my $head = $entity->head;
 
 
     my $head = $entity->head;
 
-    # convert at least MIME word encoded attachment filename
-    foreach my $attr (qw(content-type.name content-disposition.filename)) {
-       if ( my $name = $head->mime_attr($attr) and !$preserve_words ) {
-           $head->mime_attr( $attr => DecodeMIMEWordsToUTF8($name) );
-       }
-    }
-
     # If this is a textual entity, we'd need to preserve its original encoding
     $head->replace( "X-RT-Original-Encoding" => $charset )
        if $head->mime_attr('content-type.charset') or IsTextualContentType($head->mime_type);
     # If this is a textual entity, we'd need to preserve its original encoding
     $head->replace( "X-RT-Original-Encoding" => $charset )
        if $head->mime_attr('content-type.charset') or IsTextualContentType($head->mime_type);
@@ -292,7 +285,28 @@ sub DecodeMIMEWordsToEncoding {
     my $to_charset = _CanonicalizeCharset(shift);
     my $field = shift || '';
 
     my $to_charset = _CanonicalizeCharset(shift);
     my $field = shift || '';
 
-    my @list = $str =~ m/(.*?)=\?([^?]+)\?([QqBb])\?([^?]+)\?=([^=]*)/gcs;
+    # handle filename*=ISO-8859-1''%74%E9%73%74%2E%74%78%74, parameter value
+    # continuations, and similar syntax from RFC 2231
+    if ($field =~ /^Content-(Type|Disposition)/i) {
+        # This concatenates continued parameters and normalizes encoded params
+        # to QB encoded-words which we handle below
+        $str = MIME::Field::ParamVal->parse($str)->stringify;
+    }
+
+    # XXX TODO: use decode('MIME-Header', ...) and Encode::Alias to replace our
+    # custom MIME word decoding and charset canonicalization.  We can't do this
+    # until we parse before decode, instead of the other way around.
+    my @list = $str =~ m/(.*?)          # prefix
+                         =\?            # =?
+                         ([^?]+?)       # charset
+                         (?:\*[^?]+)?   # optional '*language'
+                         \?             # ?
+                         ([QqBb])       # encoding
+                         \?             # ?
+                         ([^?]+)        # encoded string
+                         \?=            # ?=
+                         ([^=]*)        # trailing
+                        /xgcs;
 
     if ( @list ) {
         # add everything that hasn't matched to the end of the latest
 
     if ( @list ) {
         # add everything that hasn't matched to the end of the latest
@@ -350,27 +364,6 @@ sub DecodeMIMEWordsToEncoding {
         }
     }
 
         }
     }
 
-# handle filename*=ISO-8859-1''%74%E9%73%74%2E%74%78%74, see also rfc 2231
-    @list = $str =~ m/(.*?\*=)([^']*?)'([^']*?)'(\S+)(.*?)(?=(?:\*=|$))/gcs;
-    if (@list) {
-        $str = '';
-        while (@list) {
-            my ( $prefix, $charset, $language, $enc_str, $trailing ) =
-              splice @list, 0, 5;
-            $prefix =~ s/\*=$/=/; # remove the *
-            $charset = _CanonicalizeCharset($charset);
-            $enc_str =~ s/%(\w{2})/chr hex $1/eg;
-            unless ( $charset eq $to_charset ) {
-                Encode::from_to( $enc_str, $charset, $to_charset );
-            }
-            $enc_str = qq{"$enc_str"}
-              if $enc_str =~ /[,;]/
-              and $enc_str !~ /^".*"$/
-              and (!$field || $field =~ /^(?:To$|From$|B?Cc$|Content-)/i);
-            $str .= $prefix . $enc_str . $trailing;
-        }
-     }
-
     # We might have \n without trailing whitespace, which will result in
     # invalid headers.
     $str =~ s/\n//g;
     # We might have \n without trailing whitespace, which will result in
     # invalid headers.
     $str =~ s/\n//g;
index 3976ade..d12abb6 100644 (file)
@@ -252,7 +252,7 @@ sub CurrentValues {
 
 sub ConfigFile {
     require File::Spec;
 
 sub ConfigFile {
     require File::Spec;
-    return File::Spec->catfile( $RT::EtcPath, 'RT_SiteConfig.pm' );
+    return $ENV{RT_SITE_CONFIG} || File::Spec->catfile( $RT::EtcPath, 'RT_SiteConfig.pm' );
 }
 
 sub SaveConfig {
 }
 
 sub SaveConfig {
index b9145d6..02a1ec0 100755 (executable)
@@ -57,6 +57,7 @@ use RT::EmailParser;
 use File::Temp;
 use UNIVERSAL::require;
 use Mail::Mailer ();
 use File::Temp;
 use UNIVERSAL::require;
 use Mail::Mailer ();
+use Text::ParseWords qw/shellwords/;
 
 BEGIN {
     use base 'Exporter';
 
 BEGIN {
     use base 'Exporter';
@@ -404,11 +405,11 @@ sub SendEmail {
 
     if ( $mail_command eq 'sendmailpipe' ) {
         my $path = RT->Config->Get('SendmailPath');
 
     if ( $mail_command eq 'sendmailpipe' ) {
         my $path = RT->Config->Get('SendmailPath');
-        my $args = RT->Config->Get('SendmailArguments');
+        my @args = shellwords(RT->Config->Get('SendmailArguments'));
 
         # SetOutgoingMailFrom and bounces conflict, since they both want -f
         if ( $args{'Bounce'} ) {
 
         # SetOutgoingMailFrom and bounces conflict, since they both want -f
         if ( $args{'Bounce'} ) {
-            $args .= ' '. RT->Config->Get('SendmailBounceArguments');
+            push @args, shellwords(RT->Config->Get('SendmailBounceArguments'));
         } elsif ( RT->Config->Get('SetOutgoingMailFrom') ) {
             my $OutgoingMailAddress;
 
         } elsif ( RT->Config->Get('SetOutgoingMailFrom') ) {
             my $OutgoingMailAddress;
 
@@ -425,7 +426,7 @@ sub SendEmail {
 
             $OutgoingMailAddress ||= RT->Config->Get('OverrideOutgoingMailFrom')->{'Default'};
 
 
             $OutgoingMailAddress ||= RT->Config->Get('OverrideOutgoingMailFrom')->{'Default'};
 
-            $args .= " -f $OutgoingMailAddress"
+            push @args, "-f", $OutgoingMailAddress
                 if $OutgoingMailAddress;
         }
 
                 if $OutgoingMailAddress;
         }
 
@@ -437,32 +438,36 @@ sub SendEmail {
             my $from = $TransactionObj->CreatorObj->EmailAddress;
             $from =~ s/@/=/g;
             $from =~ s/\s//g;
             my $from = $TransactionObj->CreatorObj->EmailAddress;
             $from =~ s/@/=/g;
             $from =~ s/\s//g;
-            $args .= " -f $prefix$from\@$domain";
+            push @args, "-f", "$prefix$from\@$domain";
         }
 
         eval {
             # don't ignore CHLD signal to get proper exit code
             local $SIG{'CHLD'} = 'DEFAULT';
 
         }
 
         eval {
             # don't ignore CHLD signal to get proper exit code
             local $SIG{'CHLD'} = 'DEFAULT';
 
-            open( my $mail, '|-', "$path $args >/dev/null" )
-                or die "couldn't execute program: $!";
-
             # if something wrong with $mail->print we will get PIPE signal, handle it
             local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
             # if something wrong with $mail->print we will get PIPE signal, handle it
             local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
+
+            require IPC::Open2;
+            my ($mail, $stdout);
+            my $pid = IPC::Open2::open2( $stdout, $mail, $path, @args )
+                or die "couldn't execute program: $!";
+
             $args{'Entity'}->print($mail);
             $args{'Entity'}->print($mail);
+            close $mail or die "close pipe failed: $!";
 
 
-            unless ( close $mail ) {
-                die "close pipe failed: $!" if $!; # system error
+            waitpid($pid, 0);
+            if ($?) {
                 # sendmail exit statuses mostly errors with data not software
                 # TODO: status parsing: core dump, exit on signal or EX_*
                 # sendmail exit statuses mostly errors with data not software
                 # TODO: status parsing: core dump, exit on signal or EX_*
-                my $msg = "$msgid: `$path $args` exitted with code ". ($?>>8);
+                my $msg = "$msgid: `$path @args` exited with code ". ($?>>8);
                 $msg = ", interrupted by signal ". ($?&127) if $?&127;
                 $RT::Logger->error( $msg );
                 die $msg;
             }
         };
         if ( $@ ) {
                 $msg = ", interrupted by signal ". ($?&127) if $?&127;
                 $RT::Logger->error( $msg );
                 die $msg;
             }
         };
         if ( $@ ) {
-            $RT::Logger->crit( "$msgid: Could not send mail with command `$path $args`: " . $@ );
+            $RT::Logger->crit( "$msgid: Could not send mail with command `$path @args`: " . $@ );
             if ( $TicketObj ) {
                 _RecordSendEmailFailure( $TicketObj );
             }
             if ( $TicketObj ) {
                 _RecordSendEmailFailure( $TicketObj );
             }
@@ -743,16 +748,19 @@ sub SendForward {
     $mail->add_part( $entity );
 
     my $from;
     $mail->add_part( $entity );
 
     my $from;
-    my $subject = '';
-    $subject = $txn->Subject if $txn;
-    $subject ||= $ticket->Subject if $ticket;
+    unless (defined $mail->head->get('Subject')) {
+        my $subject = '';
+        $subject = $txn->Subject if $txn;
+        $subject ||= $ticket->Subject if $ticket;
+
+        unless ( RT->Config->Get('ForwardFromUser') ) {
+            # XXX: what if want to forward txn of other object than ticket?
+            $subject = AddSubjectTag( $subject, $ticket );
+        }
 
 
-    unless ( RT->Config->Get('ForwardFromUser') ) {
-        # XXX: what if want to forward txn of other object than ticket?
-        $subject = AddSubjectTag( $subject, $ticket );
+        $mail->head->set( Subject => EncodeToMIME( String => "Fwd: $subject" ) );
     }
 
     }
 
-    $mail->head->set( Subject => EncodeToMIME( String => "Fwd: $subject" ) );
     $mail->head->set(
         From => EncodeToMIME(
             String => GetForwardFrom( Transaction => $txn, Ticket => $ticket )
     $mail->head->set(
         From => EncodeToMIME(
             String => GetForwardFrom( Transaction => $txn, Ticket => $ticket )
index 7c9d578..814a829 100644 (file)
@@ -158,6 +158,25 @@ sub EncodeJSON {
     JSON::to_json(shift, { utf8 => 1, allow_nonref => 1 });
 }
 
     JSON::to_json(shift, { utf8 => 1, allow_nonref => 1 });
 }
 
+sub _encode_surrogates {
+    my $uni = $_[0] - 0x10000;
+    return ($uni /  0x400 + 0xD800, $uni % 0x400 + 0xDC00);
+}
+
+sub EscapeJS {
+    my $ref = shift;
+    return unless defined $$ref;
+
+    $$ref = "'" . join('',
+                 map {
+                     chr($_) =~ /[a-zA-Z0-9]/ ? chr($_) :
+                     $_  <= 255   ? sprintf("\\x%02X", $_) :
+                     $_  <= 65535 ? sprintf("\\u%04X", $_) :
+                     sprintf("\\u%X\\u%X", _encode_surrogates($_))
+                 } unpack('U*', $$ref))
+        . "'";
+}
+
 =head2 WebCanonicalizeInfo();
 
 Different web servers set different environmental varibles. This
 =head2 WebCanonicalizeInfo();
 
 Different web servers set different environmental varibles. This
@@ -234,8 +253,10 @@ sub HandleRequest {
     ValidateWebConfig();
 
     DecodeARGS($ARGS);
     ValidateWebConfig();
 
     DecodeARGS($ARGS);
+    local $HTML::Mason::Commands::DECODED_ARGS = $ARGS;
     PreprocessTimeUpdates($ARGS);
 
     PreprocessTimeUpdates($ARGS);
 
+    InitializeMenu();
     MaybeShowInstallModePage();
 
     $HTML::Mason::Commands::m->comp( '/Elements/SetupSessionCookie', %$ARGS );
     MaybeShowInstallModePage();
 
     $HTML::Mason::Commands::m->comp( '/Elements/SetupSessionCookie', %$ARGS );
@@ -285,6 +306,8 @@ sub HandleRequest {
         }
     }
 
         }
     }
 
+    MaybeShowInterstitialCSRFPage($ARGS);
+
     # now it applies not only to home page, but any dashboard that can be used as a workspace
     $HTML::Mason::Commands::session{'home_refresh_interval'} = $ARGS->{'HomeRefreshInterval'}
         if ( $ARGS->{'HomeRefreshInterval'} );
     # now it applies not only to home page, but any dashboard that can be used as a workspace
     $HTML::Mason::Commands::session{'home_refresh_interval'} = $ARGS->{'HomeRefreshInterval'}
         if ( $ARGS->{'HomeRefreshInterval'} );
@@ -347,8 +370,6 @@ sub SetNextPage {
 
     $HTML::Mason::Commands::session{'NextPage'}->{$hash} = $next;
     $HTML::Mason::Commands::session{'i'}++;
 
     $HTML::Mason::Commands::session{'NextPage'}->{$hash} = $next;
     $HTML::Mason::Commands::session{'i'}++;
-    
-    SendSessionCookie();
     return $hash;
 }
 
     return $hash;
 }
 
@@ -465,7 +486,6 @@ sub MaybeShowNoAuthPage {
         if $m->base_comp->path eq '/NoAuth/Login.html' and _UserLoggedIn();
 
     # If it's a noauth file, don't ask for auth.
         if $m->base_comp->path eq '/NoAuth/Login.html' and _UserLoggedIn();
 
     # If it's a noauth file, don't ask for auth.
-    SendSessionCookie();
     $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %$ARGS );
     $m->abort;
 }
     $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %$ARGS );
     $m->abort;
 }
@@ -492,7 +512,7 @@ sub MaybeRejectPrivateComponentRequest {
               _elements   | # mobile UI
               Widgets     |
               autohandler | # requesting this directly is suspicious
               _elements   | # mobile UI
               Widgets     |
               autohandler | # requesting this directly is suspicious
-              l           ) # loc component
+              l (_unsafe)? ) # loc component
             ( $ | / ) # trailing slash or end of path
         }xi
         && $path !~ m{ /RTx/Statistics/\w+/Elements/Chart }xi
             ( $ | / ) # trailing slash or end of path
         }xi
         && $path !~ m{ /RTx/Statistics/\w+/Elements/Chart }xi
@@ -526,13 +546,13 @@ sub ShowRequestedPage {
 
     my $m = $HTML::Mason::Commands::m;
 
 
     my $m = $HTML::Mason::Commands::m;
 
+    # Ensure that the cookie that we send is up-to-date, in case the
+    # session-id has been modified in any way
+    SendSessionCookie();
+
     # precache all system level rights for the current user
     $HTML::Mason::Commands::session{CurrentUser}->PrincipalObj->HasRights( Object => RT->System );
 
     # precache all system level rights for the current user
     $HTML::Mason::Commands::session{CurrentUser}->PrincipalObj->HasRights( Object => RT->System );
 
-    InitializeMenu();
-
-    SendSessionCookie();
-
     # If the user isn't privileged, they can only see SelfService
     unless ( $HTML::Mason::Commands::session{'CurrentUser'}->Privileged ) {
 
     # If the user isn't privileged, they can only see SelfService
     unless ( $HTML::Mason::Commands::session{'CurrentUser'}->Privileged ) {
 
@@ -681,7 +701,6 @@ sub AttemptPasswordAuthentication {
 
         InstantiateNewSession();
         $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj;
 
         InstantiateNewSession();
         $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj;
-        SendSessionCookie();
 
         $m->callback( %$ARGS, CallbackName => 'SuccessfulLogin', CallbackPage => '/autohandler' );
 
 
         $m->callback( %$ARGS, CallbackName => 'SuccessfulLogin', CallbackPage => '/autohandler' );
 
@@ -736,6 +755,7 @@ sub LoadSessionFromCookie {
 sub InstantiateNewSession {
     tied(%HTML::Mason::Commands::session)->delete if tied(%HTML::Mason::Commands::session);
     tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', undef;
 sub InstantiateNewSession {
     tied(%HTML::Mason::Commands::session)->delete if tied(%HTML::Mason::Commands::session);
     tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', undef;
+    SendSessionCookie();
 }
 
 sub SendSessionCookie {
 }
 
 sub SendSessionCookie {
@@ -817,6 +837,10 @@ sub StaticFileHeaders {
     # make cache public
     $HTML::Mason::Commands::r->headers_out->{'Cache-Control'} = 'max-age=259200, public';
 
     # make cache public
     $HTML::Mason::Commands::r->headers_out->{'Cache-Control'} = 'max-age=259200, public';
 
+    # remove any cookie headers -- if it is cached publicly, it
+    # shouldn't include anyone's cookie!
+    delete $HTML::Mason::Commands::r->err_headers_out->{'Set-Cookie'};
+
     # Expire things in a month.
     $date->Set( Value => time + 30 * 24 * 60 * 60 );
     $HTML::Mason::Commands::r->headers_out->{'Expires'} = $date->RFC2616;
     # Expire things in a month.
     $date->Set( Value => time + 30 * 24 * 60 * 60 );
     $HTML::Mason::Commands::r->headers_out->{'Expires'} = $date->RFC2616;
@@ -828,6 +852,22 @@ sub StaticFileHeaders {
     # $HTML::Mason::Commands::r->headers_out->{'Last-Modified'} = $date->RFC2616;
 }
 
     # $HTML::Mason::Commands::r->headers_out->{'Last-Modified'} = $date->RFC2616;
 }
 
+=head2 ComponentPathIsSafe PATH
+
+Takes C<PATH> and returns a boolean indicating that the user-specified partial
+component path is safe.
+
+Currently "safe" means that the path does not start with a dot (C<.>) and does
+not contain a slash-dot C</.>.
+
+=cut
+
+sub ComponentPathIsSafe {
+    my $self = shift;
+    my $path = shift;
+    return $path !~ m{(?:^|/)\.};
+}
+
 =head2 PathIsSafe
 
 Takes a C<< Path => path >> and returns a boolean indicating that
 =head2 PathIsSafe
 
 Takes a C<< Path => path >> and returns a boolean indicating that
@@ -1138,6 +1178,218 @@ sub ComponentRoots {
     return @roots;
 }
 
     return @roots;
 }
 
+our %is_whitelisted_component = (
+    # The RSS feed embeds an auth token in the path, but query
+    # 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,
+);
+
+sub IsCompCSRFWhitelisted {
+    my $comp = shift;
+    my $ARGS = shift;
+
+    return 1 if $is_whitelisted_component{$comp};
+
+    my %args = %{ $ARGS };
+
+    # If the user specifies a *correct* user and pass then they are
+    # golden.  This acts on the presumption that external forms may
+    # hardcode a username and password -- if a malicious attacker knew
+    # both already, CSRF is the least of your problems.
+    my $AllowLoginCSRF = not RT->Config->Get('RestrictReferrerLogin');
+    if ($AllowLoginCSRF and defined($args{user}) and defined($args{pass})) {
+        my $user_obj = RT::CurrentUser->new();
+        $user_obj->Load($args{user});
+        return 1 if $user_obj->id && $user_obj->IsPassword($args{pass});
+
+        delete $args{user};
+        delete $args{pass};
+    }
+
+    # Eliminate arguments that do not indicate an effectful request.
+    # For example, "id" is acceptable because that is how RT retrieves a
+    # record.
+    delete $args{id};
+
+    # If they have a valid results= from MaybeRedirectForResults, that's
+    # also fine.
+    delete $args{results} if $args{results}
+        and $HTML::Mason::Commands::session{"Actions"}->{$args{results}};
+
+    # The homepage refresh, which uses the Refresh header, doesn't send
+    # a referer in most browsers; whitelist the one parameter it reloads
+    # with, HomeRefreshInterval, which is safe
+    delete $args{HomeRefreshInterval};
+
+    # If there are no arguments, then it's likely to be an idempotent
+    # request, which are not susceptible to CSRF
+    return 1 if !%args;
+
+    return 0;
+}
+
+sub IsRefererCSRFWhitelisted {
+    my $referer = _NormalizeHost(shift);
+    my $base_url = _NormalizeHost(RT->Config->Get('WebBaseURL'));
+    $base_url = $base_url->host_port;
+
+    my $configs;
+    for my $config ( $base_url, RT->Config->Get('ReferrerWhitelist') ) {
+        push @$configs,$config;
+        return 1 if $referer->host_port eq $config;
+    }
+
+    return (0,$referer,$configs);
+}
+
+=head3 _NormalizeHost
+
+Takes a URI and creates a URI object that's been normalized
+to handle common problems such as localhost vs 127.0.0.1
+
+=cut
+
+sub _NormalizeHost {
+
+    my $uri= URI->new(shift);
+    $uri->host('127.0.0.1') if $uri->host eq 'localhost';
+
+    return $uri;
+
+}
+
+sub IsPossibleCSRF {
+    my $ARGS = shift;
+
+    # If first request on this session is to a REST endpoint, then
+    # whitelist the REST endpoints -- and explicitly deny non-REST
+    # endpoints.  We do this because using a REST cookie in a browser
+    # would open the user to CSRF attacks to the REST endpoints.
+    my $path = $HTML::Mason::Commands::r->path_info;
+    $HTML::Mason::Commands::session{'REST'} = $path =~ m{^/+REST/\d+\.\d+(/|$)}
+        unless defined $HTML::Mason::Commands::session{'REST'};
+
+    if ($HTML::Mason::Commands::session{'REST'}) {
+        return 0 if $path =~ m{^/+REST/\d+\.\d+(/|$)};
+        my $why = <<EOT;
+This login session belongs to a REST client, and cannot be used to
+access non-REST interfaces of RT for security reasons.
+EOT
+        my $details = <<EOT;
+Please log out and back in to obtain a session for normal browsing.  If
+you understand the security implications, disabling RT's CSRF protection
+will remove this restriction.
+EOT
+        chomp $details;
+        HTML::Mason::Commands::Abort( $why, Details => $details );
+    }
+
+    return 0 if IsCompCSRFWhitelisted(
+        $HTML::Mason::Commands::m->request_comp->path,
+        $ARGS
+    );
+
+    # if there is no Referer header then assume the worst
+    return (1,
+            "your browser did not supply a Referrer header", # loc
+        ) if !$ENV{HTTP_REFERER};
+
+    my ($whitelisted, $browser, $configs) = IsRefererCSRFWhitelisted($ENV{HTTP_REFERER});
+    return 0 if $whitelisted;
+
+    if ( @$configs > 1 ) {
+        return (1,
+                "the Referrer header supplied by your browser ([_1]) is not allowed by RT's configured hostname ([_2]) or whitelisted hosts ([_3])", # loc
+                $browser->host_port,
+                shift @$configs,
+                join(', ', @$configs) );
+    }
+
+    return (1,
+            "the Referrer header supplied by your browser ([_1]) is not allowed by RT's configured hostname ([_2])", # loc
+            $browser->host_port,
+            $configs->[0]);
+}
+
+sub ExpandCSRFToken {
+    my $ARGS = shift;
+
+    my $token = delete $ARGS->{CSRF_Token};
+    return unless $token;
+
+    my $data = $HTML::Mason::Commands::session{'CSRF'}{$token};
+    return unless $data;
+    return unless $data->{path} eq $HTML::Mason::Commands::r->path_info;
+
+    my $user = $HTML::Mason::Commands::session{'CurrentUser'}->UserObj;
+    return unless $user->ValidateAuthString( $data->{auth}, $token );
+
+    %{$ARGS} = %{$data->{args}};
+    $HTML::Mason::Commands::DECODED_ARGS = $ARGS;
+
+    # We explicitly stored file attachments with the request, but not in
+    # the session yet, as that would itself be an attack.  Put them into
+    # the session now, so they'll be visible.
+    if ($data->{attach}) {
+        my $filename = $data->{attach}{filename};
+        my $mime     = $data->{attach}{mime};
+        $HTML::Mason::Commands::session{'Attachments'}{$filename}
+            = $mime;
+    }
+
+    return 1;
+}
+
+sub StoreRequestToken {
+    my $ARGS = shift;
+
+    my $token = Digest::MD5::md5_hex(time . {} . $$ . rand(1024));
+    my $user = $HTML::Mason::Commands::session{'CurrentUser'}->UserObj;
+    my $data = {
+        auth => $user->GenerateAuthString( $token ),
+        path => $HTML::Mason::Commands::r->path_info,
+        args => $ARGS,
+    };
+    if ($ARGS->{Attach}) {
+        my $attachment = HTML::Mason::Commands::MakeMIMEEntity( AttachmentFieldName => 'Attach' );
+        my $file_path = delete $ARGS->{'Attach'};
+        $data->{attach} = {
+            filename => Encode::decode_utf8("$file_path"),
+            mime     => $attachment,
+        };
+    }
+
+    $HTML::Mason::Commands::session{'CSRF'}->{$token} = $data;
+    $HTML::Mason::Commands::session{'i'}++;
+    return $token;
+}
+
+sub MaybeShowInterstitialCSRFPage {
+    my $ARGS = shift;
+
+    return unless RT->Config->Get('RestrictReferrer');
+
+    # Deal with the form token provided by the interstitial, which lets
+    # browsers which never set referer headers still use RT, if
+    # painfully.  This blows values into ARGS
+    return if ExpandCSRFToken($ARGS);
+
+    my ($is_csrf, $msg, @loc) = IsPossibleCSRF($ARGS);
+    return if !$is_csrf;
+
+    $RT::Logger->notice("Possible CSRF: ".RT::CurrentUser->new->loc($msg, @loc));
+
+    my $token = StoreRequestToken($ARGS);
+    $HTML::Mason::Commands::m->comp(
+        '/Elements/CSRF',
+        OriginalURL => RT->Config->Get('WebPath') . $HTML::Mason::Commands::r->path_info,
+        Reason => HTML::Mason::Commands::loc( $msg, @loc ),
+        Token => $token,
+    );
+    # Calls abort, never gets here
+}
+
 package HTML::Mason::Commands;
 
 use vars qw/$r $m %session/;
 package HTML::Mason::Commands;
 
 use vars qw/$r $m %session/;
@@ -1418,6 +1670,7 @@ sub CreateTicket {
             my $cfid = $1;
 
             my $cf = RT::CustomField->new( $session{'CurrentUser'} );
             my $cfid = $1;
 
             my $cf = RT::CustomField->new( $session{'CurrentUser'} );
+            $cf->SetContextObject( $Queue );
             $cf->Load($cfid);
             unless ( $cf->id ) {
                 $RT::Logger->error( "Couldn't load custom field #" . $cfid );
             $cf->Load($cfid);
             unless ( $cf->id ) {
                 $RT::Logger->error( "Couldn't load custom field #" . $cfid );
@@ -2144,10 +2397,11 @@ sub ProcessTicketReminders {
 
     if ( $args->{'update-reminders'} ) {
         while ( my $reminder = $reminder_collection->Next ) {
 
     if ( $args->{'update-reminders'} ) {
         while ( my $reminder = $reminder_collection->Next ) {
-            if (   $reminder->Status ne 'resolved' && $args->{ 'Complete-Reminder-' . $reminder->id } ) {
+            my $resolve_status = $reminder->QueueObj->Lifecycle->ReminderStatusOnResolve;
+            if (   $reminder->Status ne $resolve_status && $args->{ 'Complete-Reminder-' . $reminder->id } ) {
                 $Ticket->Reminders->Resolve($reminder);
             }
                 $Ticket->Reminders->Resolve($reminder);
             }
-            elsif ( $reminder->Status eq 'resolved' && !$args->{ 'Complete-Reminder-' . $reminder->id } ) {
+            elsif ( $reminder->Status eq $resolve_status && !$args->{ 'Complete-Reminder-' . $reminder->id } ) {
                 $Ticket->Reminders->Open($reminder);
             }
 
                 $Ticket->Reminders->Open($reminder);
             }
 
@@ -2239,6 +2493,7 @@ sub ProcessObjectCustomFieldUpdates {
 
             foreach my $cf ( keys %{ $custom_fields_to_mod{$class}{$id} } ) {
                 my $CustomFieldObj = RT::CustomField->new( $session{'CurrentUser'} );
 
             foreach my $cf ( keys %{ $custom_fields_to_mod{$class}{$id} } ) {
                 my $CustomFieldObj = RT::CustomField->new( $session{'CurrentUser'} );
+                $CustomFieldObj->SetContextObject($Object);
                 $CustomFieldObj->LoadById($cf);
                 unless ( $CustomFieldObj->id ) {
                     $RT::Logger->warning("Couldn't load custom field #$cf");
                 $CustomFieldObj->LoadById($cf);
                 unless ( $CustomFieldObj->id ) {
                     $RT::Logger->warning("Couldn't load custom field #$cf");
@@ -2851,50 +3106,71 @@ sub ScrubHTML {
 
 =head2 _NewScrubber
 
 
 =head2 _NewScrubber
 
-Returns a new L<HTML::Scrubber> object.  Override this if you insist on
-letting more HTML through.
+Returns a new L<HTML::Scrubber> object.
+
+If you need to be more lax about what HTML tags and attributes are allowed,
+create C</opt/rt4/local/lib/RT/Interface/Web_Local.pm> with something like the
+following:
+
+    package HTML::Mason::Commands;
+    # Let tables through
+    push @SCRUBBER_ALLOWED_TAGS, qw(TABLE THEAD TBODY TFOOT TR TD TH);
+    1;
 
 =cut
 
 
 =cut
 
+our @SCRUBBER_ALLOWED_TAGS = qw(
+    A B U P BR I HR BR SMALL EM FONT SPAN STRONG SUB SUP STRIKE H1 H2 H3 H4 H5
+    H6 DIV UL OL LI DL DT DD PRE BLOCKQUOTE BDO
+);
+
+our %SCRUBBER_ALLOWED_ATTRIBUTES = (
+    # Match http, ftp and relative urls
+    # XXX: we also scrub format strings with this module then allow simple config options
+    href   => qr{^(?:http:|ftp:|https:|/|__Web(?:Path|BaseURL|URL)__)}i,
+    face   => 1,
+    size   => 1,
+    target => 1,
+    style  => qr{
+        ^(?:\s*
+            (?:(?:background-)?color: \s*
+                    (?:rgb\(\s* \d+, \s* \d+, \s* \d+ \s*\) |   # rgb(d,d,d)
+                       \#[a-f0-9]{3,6}                      |   # #fff or #ffffff
+                       [\w\-]+                                  # green, light-blue, etc.
+                       )                            |
+               text-align: \s* \w+                  |
+               font-size: \s* [\w.\-]+              |
+               font-family: \s* [\w\s"',.\-]+       |
+               font-weight: \s* [\w\-]+             |
+
+               # MS Office styles, which are probably fine.  If we don't, then any
+               # associated styles in the same attribute get stripped.
+               mso-[\w\-]+?: \s* [\w\s"',.\-]+
+            )\s* ;? \s*)
+         +$ # one or more of these allowed properties from here 'till sunset
+    }ix,
+    dir    => qr/^(rtl|ltr)$/i,
+    lang   => qr/^\w+(-\w+)?$/,
+);
+
+our %SCRUBBER_RULES = ();
+
 sub _NewScrubber {
     require HTML::Scrubber;
     my $scrubber = HTML::Scrubber->new();
     $scrubber->default(
         0,
         {
 sub _NewScrubber {
     require HTML::Scrubber;
     my $scrubber = HTML::Scrubber->new();
     $scrubber->default(
         0,
         {
-            '*'    => 0,
-            id     => 1,
-            class  => 1,
-            # Match http, ftp and relative urls
-            # XXX: we also scrub format strings with this module then allow simple config options
-            href   => qr{^(?:http:|ftp:|https:|/|__Web(?:Path|BaseURL|URL)__)}i,
-            face   => 1,
-            size   => 1,
-            target => 1,
-            style  => qr{
-                ^(?:\s*
-                    (?:(?:background-)?color: \s*
-                            (?:rgb\(\s* \d+, \s* \d+, \s* \d+ \s*\) |   # rgb(d,d,d)
-                               \#[a-f0-9]{3,6}                      |   # #fff or #ffffff
-                               [\w\-]+                                  # green, light-blue, etc.
-                               )                            |
-                       text-align: \s* \w+                  |
-                       font-size: \s* [\w.\-]+              |
-                       font-family: \s* [\w\s"',.\-]+       |
-                       font-weight: \s* [\w\-]+             |
-
-                       # MS Office styles, which are probably fine.  If we don't, then any
-                       # associated styles in the same attribute get stripped.
-                       mso-[\w\-]+?: \s* [\w\s"',.\-]+
-                    )\s* ;? \s*)
-                 +$ # one or more of these allowed properties from here 'till sunset
-            }ix,
-        }
+            %SCRUBBER_ALLOWED_ATTRIBUTES,
+            '*' => 0, # require attributes be explicitly allowed
+        },
     );
     $scrubber->deny(qw[*]);
     );
     $scrubber->deny(qw[*]);
-    $scrubber->allow(
-        qw[A B U P BR I HR BR SMALL EM FONT SPAN STRONG SUB SUP STRIKE H1 H2 H3 H4 H5 H6 DIV UL OL LI DL DT DD PRE BLOCKQUOTE]
-    );
+    $scrubber->allow(@SCRUBBER_ALLOWED_TAGS);
+    $scrubber->rules(%SCRUBBER_RULES);
+
+    # Scrubbing comments is vital since IE conditional comments can contain
+    # arbitrary HTML and we'd pass it right on through.
     $scrubber->comment(0);
 
     return $scrubber;
     $scrubber->comment(0);
 
     return $scrubber;
index 69eee60..a740167 100644 (file)
@@ -69,12 +69,12 @@ sub DefaultHandlerArgs  { (
     ],
     default_escape_flags => 'h',
     data_dir             => "$RT::MasonDataDir",
     ],
     default_escape_flags => 'h',
     data_dir             => "$RT::MasonDataDir",
-    allow_globals        => [qw(%session)],
+    allow_globals        => [qw(%session $DECODED_ARGS)],
     # Turn off static source if we're in developer mode.
     static_source        => (RT->Config->Get('DevelMode') ? '0' : '1'), 
     use_object_files     => (RT->Config->Get('DevelMode') ? '0' : '1'), 
     autoflush            => 0,
     # Turn off static source if we're in developer mode.
     static_source        => (RT->Config->Get('DevelMode') ? '0' : '1'), 
     use_object_files     => (RT->Config->Get('DevelMode') ? '0' : '1'), 
     autoflush            => 0,
-    error_format         => (RT->Config->Get('DevelMode') ? 'html': 'brief'),
+    error_format         => (RT->Config->Get('DevelMode') ? 'html': 'rt_error'),
     request_class        => 'RT::Interface::Web::Request',
     named_component_subs => $INC{'Devel/Cover.pm'} ? 1 : 0,
 ) };
     request_class        => 'RT::Interface::Web::Request',
     named_component_subs => $INC{'Devel/Cover.pm'} ? 1 : 0,
 ) };
@@ -116,6 +116,7 @@ sub NewHandler {
   
     $handler->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
     $handler->interp->set_escape( u => \&RT::Interface::Web::EscapeURI  );
   
     $handler->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
     $handler->interp->set_escape( u => \&RT::Interface::Web::EscapeURI  );
+    $handler->interp->set_escape( j => \&RT::Interface::Web::EscapeJS   );
     return($handler);
 }
 
     return($handler);
 }
 
@@ -202,6 +203,13 @@ sub CleanupRequest {
 }
 
 
 }
 
 
+sub HTML::Mason::Exception::as_rt_error {
+    my ($self) = @_;
+    $RT::Logger->error( $self->full_message );
+    return "An internal RT error has occurred.  Your administrator can find more details in RT's log files.";
+}
+
+
 # PSGI App
 
 use RT::Interface::Web::Handler;
 # PSGI App
 
 use RT::Interface::Web::Handler;
index e2ec1e5..2cfc889 100755 (executable)
@@ -92,8 +92,8 @@ sub TraversePrePost {
 
 =head2 GetReferencedQueues
 
 
 =head2 GetReferencedQueues
 
-Returns a hash reference with keys each queue name referenced in a clause in
-the key (even if it's "Queue != 'Foo'"), and values all 1.
+Returns a hash reference; each queue referenced with an '=' operation
+will appear as a key whose value is 1.
 
 =cut
 
 
 =cut
 
@@ -110,10 +110,12 @@ sub GetReferencedQueues {
             return unless $node->isLeaf;
 
             my $clause = $node->getNodeValue();
             return unless $node->isLeaf;
 
             my $clause = $node->getNodeValue();
+            return unless $clause->{Key} eq 'Queue';
+            return unless $clause->{Op} eq '=';
 
 
-            if ( $clause->{Key} eq 'Queue' ) {
-                $queues->{ $clause->{Value} } = 1;
-            };
+            my $value = $clause->{Value};
+            $value =~ s/\\(.)/$1/g if $value =~ s/^'(.*)'$/$1/;
+            $queues->{ $value } = 1;
         }
     );
 
         }
     );
 
@@ -275,7 +277,7 @@ sub ParseSQL {
             $value = "'$value'";
         }
 
             $value = "'$value'";
         }
 
-        if ($key =~ s/(['\\])/\\$1/g or $key =~ /\s/) {
+        if ($key =~ s/(['\\])/\\$1/g or $key =~ /[^{}\w\.]/) {
             $key = "'$key'";
         }
 
             $key = "'$key'";
         }
 
index edb1795..056599e 100644 (file)
@@ -367,6 +367,28 @@ sub DefaultOnMerge {
     return $self->DefaultStatus('on_merge');
 }
 
     return $self->DefaultStatus('on_merge');
 }
 
+=head3 ReminderStatusOnOpen
+
+Returns the status that should be used when reminders are opened.
+
+=cut
+
+sub ReminderStatusOnOpen {
+    my $self = shift;
+    return $self->DefaultStatus('reminder_on_open') || 'open';
+}
+
+=head3 ReminderStatusOnResolve
+
+Returns the status that should be used when reminders are resolved.
+
+=cut
+
+sub ReminderStatusOnResolve {
+    my $self = shift;
+    return $self->DefaultStatus('reminder_on_resolve') || 'resolved';
+}
+
 =head2 Transitions, rights, labels and actions.
 
 =head3 Transitions
 =head2 Transitions, rights, labels and actions.
 
 =head3 Transitions
index 0d8ed2f..ccc72d7 100644 (file)
@@ -91,15 +91,17 @@ sub Limit  {
 
     if ( ($args{'FIELD'} eq 'Target') or 
         ($args{'FIELD'} eq 'LocalTarget') ) {
 
     if ( ($args{'FIELD'} eq 'Target') or 
         ($args{'FIELD'} eq 'LocalTarget') ) {
-       $self->OrderBy (ALIAS => 'main',
-                       FIELD => 'Base',
-                       ORDER => 'ASC');
+       $self->OrderByCols(
+            { ALIAS => 'main', FIELD => 'LocalBase', ORDER => 'ASC' },
+            { ALIAS => 'main', FIELD => 'Base', ORDER => 'ASC' },
+        );
     }
     elsif ( ($args{'FIELD'} eq 'Base') or 
            ($args{'FIELD'} eq 'LocalBase') ) {
     }
     elsif ( ($args{'FIELD'} eq 'Base') or 
            ($args{'FIELD'} eq 'LocalBase') ) {
-       $self->OrderBy (ALIAS => 'main',
-                       FIELD => 'Target',
-                       ORDER => 'ASC');
+       $self->OrderByCols(
+            { ALIAS => 'main', FIELD => 'LocalTarget', ORDER => 'ASC' },
+            { ALIAS => 'main', FIELD => 'Target', ORDER => 'ASC' },
+        );
     }
     
 
     }
     
 
index 0b815ae..61bc355 100644 (file)
@@ -137,7 +137,19 @@ Returns the CustomField Object which has the id returned by CustomField
 sub CustomFieldObj {
     my $self = shift;
     my $id = shift || $self->CustomField;
 sub CustomFieldObj {
     my $self = shift;
     my $id = shift || $self->CustomField;
+
+    # To find out the proper context object to load the CF with, we need
+    # data from the CF -- namely, the record class.  Go find that as the
+    # system user first.
+    my $system_CF = RT::CustomField->new( RT->SystemUser );
+    $system_CF->Load( $id );
+    my $class = $system_CF->RecordClassFromLookupType;
+
+    my $obj = $class->new( $self->CurrentUser );
+    $obj->Load( $self->ObjectId );
+
     my $CF = RT::CustomField->new( $self->CurrentUser );
     my $CF = RT::CustomField->new( $self->CurrentUser );
+    $CF->SetContextObject( $obj );
     $CF->Load( $id );
     return $CF;
 }
     $CF->Load( $id );
     return $CF;
 }
index 0fd9d73..98714a0 100644 (file)
@@ -251,6 +251,8 @@ my $re_ip_serialized = qr/$re_ip_sunit(?:\.$re_ip_sunit){3}/;
 sub Content {
     my $self = shift;
 
 sub Content {
     my $self = shift;
 
+    return undef unless $self->CustomFieldObj->CurrentUserHasRight('SeeCustomField');
+
     my $content = $self->_Value('Content');
     if (   $self->CustomFieldObj->Type eq 'IPAddress'
         || $self->CustomFieldObj->Type eq 'IPAddressRange' )
     my $content = $self->_Value('Content');
     if (   $self->CustomFieldObj->Type eq 'IPAddress'
         || $self->CustomFieldObj->Type eq 'IPAddressRange' )
@@ -364,11 +366,11 @@ sub _FillInTemplateURL {
     # special case, whole value should be an URL
     if ( $url =~ /^__CustomField__/ ) {
         my $value = $self->Content;
     # special case, whole value should be an URL
     if ( $url =~ /^__CustomField__/ ) {
         my $value = $self->Content;
-        # protect from javascript: URLs
-        if ( $value =~ /^\s*javascript:/i ) {
+        # protect from potentially malicious URLs
+        if ( $value =~ /^\s*(?:javascript|data):/i ) {
             my $object = $self->Object;
             $RT::Logger->error(
             my $object = $self->Object;
             $RT::Logger->error(
-                "Dangerouse value with JavaScript in custom field '". $self->CustomFieldObj->Name ."'"
+                "Potentially dangerous URL type in custom field '". $self->CustomFieldObj->Name ."'"
                 ." on ". ref($object) ." #". $object->id
             );
             return undef;
                 ." on ". ref($object) ." #". $object->id
             );
             return undef;
index 3cb87c4..406df92 100755 (executable)
@@ -692,6 +692,7 @@ sub TicketTransactionCustomFields {
 
     my $cfs = RT::CustomFields->new( $self->CurrentUser );
     if ( $self->CurrentUserHasRight('SeeQueue') ) {
 
     my $cfs = RT::CustomFields->new( $self->CurrentUser );
     if ( $self->CurrentUserHasRight('SeeQueue') ) {
+        $cfs->SetContextObject( $self );
        $cfs->LimitToGlobalOrObjectId( $self->Id );
        $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket-RT::Transaction' );
         $cfs->ApplySortOrder;
        $cfs->LimitToGlobalOrObjectId( $self->Id );
        $cfs->LimitToLookupType( 'RT::Queue-RT::Ticket-RT::Transaction' );
         $cfs->ApplySortOrder;
@@ -1249,6 +1250,17 @@ sub CurrentUserHasRight {
 
 }
 
 
 }
 
+=head2 CurrentUserCanSee
+
+Returns true if the current user can see the queue, using SeeQueue
+
+=cut
+
+sub CurrentUserCanSee {
+    my $self = shift;
+
+    return $self->CurrentUserHasRight('SeeQueue');
+}
 
 
 =head2 HasRight
 
 
 =head2 HasRight
index e7c28a8..2b66325 100644 (file)
@@ -137,7 +137,8 @@ sub Open {
     my $self = shift;
     my $reminder = shift;
 
     my $self = shift;
     my $reminder = shift;
 
-    my ( $status, $msg ) = $reminder->SetStatus('open');
+    my ( $status, $msg ) =
+      $reminder->SetStatus( $reminder->QueueObj->Lifecycle->ReminderStatusOnOpen );
     $self->TicketObj->_NewTransaction(
         Type => 'OpenReminder',
         Field => 'RT::Ticket',
     $self->TicketObj->_NewTransaction(
         Type => 'OpenReminder',
         Field => 'RT::Ticket',
@@ -149,7 +150,8 @@ sub Open {
 sub Resolve {
     my $self = shift;
     my $reminder = shift;
 sub Resolve {
     my $self = shift;
     my $reminder = shift;
-    my ( $status, $msg ) = $reminder->SetStatus('resolved');
+    my ( $status, $msg ) =
+      $reminder->SetStatus( $reminder->QueueObj->Lifecycle->ReminderStatusOnResolve );
     $self->TicketObj->_NewTransaction(
         Type => 'ResolveReminder',
         Field => 'RT::Ticket',
     $self->TicketObj->_NewTransaction(
         Type => 'ResolveReminder',
         Field => 'RT::Ticket',
index af6e4ed..de40dbd 100644 (file)
@@ -89,13 +89,7 @@ sub Groupings {
         foreach my $id (keys %$queues) {
             my $queue = RT::Queue->new( $self->CurrentUser );
             $queue->Load($id);
         foreach my $id (keys %$queues) {
             my $queue = RT::Queue->new( $self->CurrentUser );
             $queue->Load($id);
-            unless ($queue->id) {
-                # XXX TODO: This ancient code dates from a former developer
-                # we have no idea what it means or why cfqueues are so encoded.
-                $id =~ s/^.'*(.*).'*$/$1/;
-                $queue->Load($id);
-            }
-            $CustomFields->LimitToQueue($queue->Id);
+            $CustomFields->LimitToQueue($queue->Id) if $queue->Id;
         }
         $CustomFields->LimitToGlobal;
         while ( my $CustomField = $CustomFields->Next ) {
         }
         $CustomFields->LimitToGlobal;
         while ( my $CustomField = $CustomFields->Next ) {
index f3eeb4d..87754c4 100644 (file)
@@ -83,6 +83,10 @@ sub LabelValue {
     return $value;
 }
 
     return $value;
 }
 
+sub ObjectType {
+    return 'RT::Ticket';
+}
+
 RT::Base->_ImportOverlays();
 
 1;
 RT::Base->_ImportOverlays();
 
 1;
index d513e0a..9506616 100755 (executable)
@@ -514,13 +514,35 @@ sub _Set {
     }
 
 
     }
 
 
-    if (length($args{Value})) {
+    if (exists $args{Value}) {
         if ($args{Field} eq 'CustomIsApplicableCode' || $args{Field} eq 'CustomPrepareCode' || $args{Field} eq 'CustomCommitCode') {
             unless ( $self->CurrentUser->HasRight( Object => $RT::System,
                                                    Right  => 'ExecuteCode' ) ) {
                 return ( 0, $self->loc('Permission Denied') );
             }
         }
         if ($args{Field} eq 'CustomIsApplicableCode' || $args{Field} eq 'CustomPrepareCode' || $args{Field} eq 'CustomCommitCode') {
             unless ( $self->CurrentUser->HasRight( Object => $RT::System,
                                                    Right  => 'ExecuteCode' ) ) {
                 return ( 0, $self->loc('Permission Denied') );
             }
         }
+        elsif ($args{Field} eq 'Queue') {
+            if ($args{Value}) {
+                # moving to another queue
+                my $queue = RT::Queue->new( $self->CurrentUser );
+                $queue->Load($args{Value});
+                unless ($queue->Id and $queue->CurrentUserHasRight('ModifyScrips')) {
+                    return ( 0, $self->loc('Permission Denied') );
+                }
+            } else {
+                # moving to global
+                unless ($self->CurrentUser->HasRight( Object => RT->System, Right => 'ModifyScrips' )) {
+                    return ( 0, $self->loc('Permission Denied') );
+                }
+            }
+        }
+        elsif ($args{Field} eq 'Template') {
+            my $template = RT::Template->new( $self->CurrentUser );
+            $template->Load($args{Value});
+            unless ($template->Id and $template->CurrentUserCanRead) {
+                return ( 0, $self->loc('Permission Denied') );
+            }
+        }
     }
 
     return $self->__Set(@_);
     }
 
     return $self->__Set(@_);
index 02d4c50..3e98551 100644 (file)
@@ -105,10 +105,14 @@ sub JoinTransactions {
         TABLE2 => 'Transactions',
         FIELD2 => 'ObjectId',
     );
         TABLE2 => 'Transactions',
         FIELD2 => 'ObjectId',
     );
+
+    my $item = $self->NewItem;
+    my $object_type = $item->can('ObjectType') ? $item->ObjectType : ref $item;
+
     $self->RT::SearchBuilder::Limit(
         LEFTJOIN => $alias,
         FIELD    => 'ObjectType',
     $self->RT::SearchBuilder::Limit(
         LEFTJOIN => $alias,
         FIELD    => 'ObjectType',
-        VALUE    => ref $self->NewItem,
+        VALUE    => $object_type,
     );
     $self->{'_sql_aliases'}{'transactions'} = $alias
         unless $args{'New'};
     );
     $self->{'_sql_aliases'}{'transactions'} = $alias
         unless $args{'New'};
@@ -127,6 +131,19 @@ sub OrderByCols {
     return $self->SUPER::OrderByCols( @sort );
 }
 
     return $self->SUPER::OrderByCols( @sort );
 }
 
+# If we're setting RowsPerPage or FirstRow, ensure we get a natural number or undef.
+sub RowsPerPage {
+    my $self = shift;
+    return if @_ and defined $_[0] and $_[0] =~ /\D/;
+    return $self->SUPER::RowsPerPage(@_);
+}
+
+sub FirstRow {
+    my $self = shift;
+    return if @_ and defined $_[0] and $_[0] =~ /\D/;
+    return $self->SUPER::FirstRow(@_);
+}
+
 =head2 LimitToEnabled
 
 Only find items that haven't been disabled
 =head2 LimitToEnabled
 
 Only find items that haven't been disabled
index 10d3536..40c73b3 100644 (file)
@@ -351,6 +351,8 @@ sub CastObjectsToRecords
     } elsif ( UNIVERSAL::isa( $targets, 'SCALAR' ) || !ref $targets ) {
         $targets = $$targets if ref $targets;
         my ($class, $id) = split /-/, $targets;
     } elsif ( UNIVERSAL::isa( $targets, 'SCALAR' ) || !ref $targets ) {
         $targets = $$targets if ref $targets;
         my ($class, $id) = split /-/, $targets;
+        RT::Shredder::Exception->throw( "Unsupported class $class" )
+              unless $class =~ /^\w+(::\w+)*$/;
         $class = 'RT::'. $class unless $class =~ /^RTx?::/i;
         eval "require $class";
         die "Couldn't load '$class' module" if $@;
         $class = 'RT::'. $class unless $class =~ /^RTx?::/i;
         eval "require $class";
         die "Couldn't load '$class' module" if $@;
index e70d207..ad9af6a 100644 (file)
@@ -167,6 +167,7 @@ sub LoadByName
 {
     my $self = shift;
     my $name = shift or return (0, "Name not specified");
 {
     my $self = shift;
     my $name = shift or return (0, "Name not specified");
+    $name =~ /^\w+(::\w+)*$/ or return (0, "Invalid plugin name");
 
     local $@;
     my $plugin = "RT::Shredder::Plugin::$name";
 
     local $@;
     my $plugin = "RT::Shredder::Plugin::$name";
index c2ec445..2c0d068 100644 (file)
@@ -91,6 +91,7 @@ sub __DependsOn
 
 # Custom Fields
     $objs = RT::CustomFields->new( $self->CurrentUser );
 
 # Custom Fields
     $objs = RT::CustomFields->new( $self->CurrentUser );
+    $objs->SetContextObject( $self );
     $objs->LimitToQueue( $self->id );
     push( @$list, $objs );
 
     $objs->LimitToQueue( $self->id );
     push( @$list, $objs );
 
index 158547a..117cc3f 100755 (executable)
@@ -96,10 +96,34 @@ sub _Accessible {
 
 sub _Set {
     my $self = shift;
 
 sub _Set {
     my $self = shift;
+    my %args = (
+        Field => undef,
+        Value => undef,
+        @_,
+    );
     
     unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
         return ( 0, $self->loc('Permission Denied') );
     }
     
     unless ( $self->CurrentUserHasQueueRight('ModifyTemplate') ) {
         return ( 0, $self->loc('Permission Denied') );
     }
+
+    if (exists $args{Value}) {
+        if ($args{Field} eq 'Queue') {
+            if ($args{Value}) {
+                # moving to another queue
+                my $queue = RT::Queue->new( $self->CurrentUser );
+                $queue->Load($args{Value});
+                unless ($queue->Id and $queue->CurrentUserHasRight('ModifyTemplate')) {
+                    return ( 0, $self->loc('Permission Denied') );
+                }
+            } else {
+                # moving to global
+                unless ($self->CurrentUser->HasRight( Object => RT->System, Right => 'ModifyTemplate' )) {
+                    return ( 0, $self->loc('Permission Denied') );
+                }
+            }
+        }
+    }
+
     return $self->SUPER::_Set( @_ );
 }
 
     return $self->SUPER::_Set( @_ );
 }
 
index 0d6da1b..7d69dd6 100644 (file)
@@ -409,7 +409,11 @@ sub bootstrap_db {
         $args{$forceopt}=1;
     }
 
         $args{$forceopt}=1;
     }
 
-    return if $args{nodb};
+    # Short-circuit the rest of ourselves if we don't want a db
+    if ($args{nodb}) {
+        __drop_database();
+        return;
+    }
 
     my $db_type = RT->Config->Get('DatabaseType');
     __create_database();
 
     my $db_type = RT->Config->Get('DatabaseType');
     __create_database();
@@ -556,6 +560,13 @@ sub __drop_database {
         RT::Handle->SystemDSN,
         $ENV{RT_DBA_USER}, $ENV{RT_DBA_PASSWORD}
     );
         RT::Handle->SystemDSN,
         $ENV{RT_DBA_USER}, $ENV{RT_DBA_PASSWORD}
     );
+
+    # We ignore errors intentionally by not checking the return value of
+    # DropDatabase below, so let's also suppress DBI's printing of errors when
+    # we overzealously drop.
+    local $dbh->{PrintError} = 0;
+    local $dbh->{PrintWarn} = 0;
+
     RT::Handle->DropDatabase( $dbh );
     $dbh->disconnect if $my_dbh;
 }
     RT::Handle->DropDatabase( $dbh );
     $dbh->disconnect if $my_dbh;
 }
@@ -1276,8 +1287,10 @@ sub started_ok {
 
     require RT::Test::Web;
 
 
     require RT::Test::Web;
 
-    if ($rttest_opt{nodb}) {
-        die "you are trying to use a test web server without db, try use noinitialdata => 1 instead";
+    if ($rttest_opt{nodb} and not $rttest_opt{server_ok}) {
+        die "You are trying to use a test web server without a database. "
+           ."You may want noinitialdata => 1 instead. "
+           ."Pass server_ok => 1 if you know what you're doing.";
     }
 
 
     }
 
 
@@ -1298,11 +1311,31 @@ sub test_app {
     my $self = shift;
     my %server_opt = @_;
 
     my $self = shift;
     my %server_opt = @_;
 
-    require RT::Interface::Web::Handler;
-    my $app = RT::Interface::Web::Handler->PSGIApp;
+    my $app;
+
+    my $warnings = "";
+    open( my $warn_fh, ">", \$warnings );
+    local *STDERR = $warn_fh;
+
+    if ($server_opt{variant} and $server_opt{variant} eq 'rt-server') {
+        $app = do {
+            my $file = "$RT::SbinPath/rt-server";
+            my $psgi = do $file;
+            unless ($psgi) {
+                die "Couldn't parse $file: $@" if $@;
+                die "Couldn't do $file: $!"    unless defined $psgi;
+                die "Couldn't run $file"       unless $psgi;
+            }
+            $psgi;
+        };
+    } else {
+        require RT::Interface::Web::Handler;
+        $app = RT::Interface::Web::Handler->PSGIApp;
+    }
 
     require Plack::Middleware::Test::StashWarnings;
 
     require Plack::Middleware::Test::StashWarnings;
-    $app = Plack::Middleware::Test::StashWarnings->wrap($app);
+    my $stashwarnings = Plack::Middleware::Test::StashWarnings->new;
+    $app = $stashwarnings->wrap($app);
 
     if ($server_opt{basic_auth}) {
         require Plack::Middleware::Auth::Basic;
 
     if ($server_opt{basic_auth}) {
         require Plack::Middleware::Auth::Basic;
@@ -1314,6 +1347,10 @@ sub test_app {
             }
         );
     }
             }
         );
     }
+
+    close $warn_fh;
+    $stashwarnings->add_warning( $warnings ) if $warnings;
+
     return $app;
 }
 
     return $app;
 }
 
@@ -1346,7 +1383,8 @@ sub start_plack_server {
         my $Tester = Test::Builder->new;
         $Tester->ok(1, "started plack server ok");
 
         my $Tester = Test::Builder->new;
         $Tester->ok(1, "started plack server ok");
 
-        __reconnect_rt();
+        __reconnect_rt()
+            unless $rttest_opt{nodb};
         return ("http://localhost:$port", RT::Test::Web->new);
     }
 
         return ("http://localhost:$port", RT::Test::Web->new);
     }
 
index 28ca3b8..c2d9ac3 100644 (file)
@@ -52,15 +52,19 @@ use strict;
 use warnings;
 
 use base qw(Test::WWW::Mechanize);
 use warnings;
 
 use base qw(Test::WWW::Mechanize);
+use Scalar::Util qw(weaken);
 
 
-require RT::Test;
+BEGIN { require RT::Test; }
 require Test::More;
 
 require Test::More;
 
+my $instance;
+
 sub new {
     my ($class, @args) = @_;
 
     push @args, app => $RT::Test::TEST_APP if $RT::Test::TEST_APP;
 sub new {
     my ($class, @args) = @_;
 
     push @args, app => $RT::Test::TEST_APP if $RT::Test::TEST_APP;
-    my $self = $class->SUPER::new(@args);
+    my $self = $instance = $class->SUPER::new(@args);
+    weaken $instance;
     $self->cookie_jar(HTTP::Cookies->new);
 
     return $self;
     $self->cookie_jar(HTTP::Cookies->new);
 
     return $self;
@@ -100,6 +104,7 @@ sub login {
         Test::More::diag("error: page has no Logout");
         return 0;
     }
         Test::More::diag("error: page has no Logout");
         return 0;
     }
+    RT::Interface::Web::EscapeUTF8(\$user);
     unless ( $self->content =~ m{<span class="current-user">\Q$user\E</span>}i ) {
         Test::More::diag("Page has no user name");
         return 0;
     unless ( $self->content =~ m{<span class="current-user">\Q$user\E</span>}i ) {
         Test::More::diag("Page has no user name");
         return 0;
@@ -370,4 +375,10 @@ sub DESTROY {
     }
 }
 
     }
 }
 
+END {
+    return unless $instance;
+    return if RT::Test->builder->{Original_Pid} != $$;
+    $instance->no_warnings_ok if !$RT::Test::Web::DESTROY++;
+}
+
 1;
 1;
index 27bdc48..de8a396 100755 (executable)
@@ -2438,7 +2438,7 @@ sub _Links {
     my $links = $self->{ $cache_key }
               = RT::Links->new( $self->CurrentUser );
     unless ( $self->CurrentUserHasRight('ShowTicket') ) {
     my $links = $self->{ $cache_key }
               = RT::Links->new( $self->CurrentUser );
     unless ( $self->CurrentUserHasRight('ShowTicket') ) {
-        $links->Limit( FIELD => 'id', VALUE => 0 );
+        $links->Limit( FIELD => 'id', VALUE => 0, SUBCLAUSE => 'acl' );
         return $links;
     }
 
         return $links;
     }
 
@@ -3594,6 +3594,16 @@ sub CurrentUserHasRight {
 }
 
 
 }
 
 
+=head2 CurrentUserCanSee
+
+Returns true if the current user can see the ticket, using ShowTicket
+
+=cut
+
+sub CurrentUserCanSee {
+    my $self = shift;
+    return $self->CurrentUserHasRight('ShowTicket');
+}
 
 =head2 HasRight
 
 
 =head2 HasRight
 
@@ -3706,7 +3716,9 @@ sub Transactions {
 
 sub TransactionCustomFields {
     my $self = shift;
 
 sub TransactionCustomFields {
     my $self = shift;
-    return $self->QueueObj->TicketTransactionCustomFields;
+    my $cfs = $self->QueueObj->TicketTransactionCustomFields;
+    $cfs->SetContextObject( $self );
+    return $cfs;
 }
 
 
 }
 
 
index db54b52..4e2415b 100755 (executable)
@@ -1132,6 +1132,12 @@ sub _GroupMembersJoin {
         FIELD2          => 'GroupId',
         ENTRYAGGREGATOR => 'AND',
     );
         FIELD2          => 'GroupId',
         ENTRYAGGREGATOR => 'AND',
     );
+    $self->SUPER::Limit(
+        $args{'Left'} ? (LEFTJOIN => $alias) : (),
+        ALIAS => $alias,
+        FIELD => 'Disabled',
+        VALUE => 0,
+    );
 
     $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} } = $alias
         unless $args{'New'};
 
     $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} } = $alias
         unless $args{'New'};
@@ -1296,6 +1302,12 @@ sub _WatcherMembershipLimit {
         FIELD2 => 'id'
     );
 
         FIELD2 => 'id'
     );
 
+    $self->Limit(
+        ALIAS => $groupmembers,
+        FIELD => 'Disabled',
+        VALUE => 0,
+    );
+
     $self->Join(
         ALIAS1 => $memberships,
         FIELD1 => 'MemberId',
     $self->Join(
         ALIAS1 => $memberships,
         FIELD1 => 'MemberId',
@@ -1303,6 +1315,13 @@ sub _WatcherMembershipLimit {
         FIELD2 => 'id'
     );
 
         FIELD2 => 'id'
     );
 
+    $self->Limit(
+        ALIAS => $memberships,
+        FIELD => 'Disabled',
+        VALUE => 0,
+    );
+
+
     $self->_CloseParen;
 
 }
     $self->_CloseParen;
 
 }
@@ -1639,11 +1658,8 @@ sub _CustomFieldLimit {
             $self->_CloseParen;
         }
         else {
             $self->_CloseParen;
         }
         else {
-            my $cf = RT::CustomField->new( $self->CurrentUser );
-            $cf->Load($field);
-
             # need special treatment for Date
             # need special treatment for Date
-            if ( $cf->Type eq 'DateTime' && $op eq '=' ) {
+            if ( $cf and $cf->Type eq 'DateTime' and $op eq '=' ) {
 
                 if ( $value =~ /:/ ) {
                     # there is time speccified.
 
                 if ( $value =~ /:/ ) {
                     # there is time speccified.
@@ -3647,7 +3663,11 @@ sub _RestrictionsToClauses {
         # here is where we store extra data, say if it's a keyword or
         # something.  (I.e. "TYPE SPECIFIC STUFF")
 
         # here is where we store extra data, say if it's a keyword or
         # something.  (I.e. "TYPE SPECIFIC STUFF")
 
-        push @{ $clause{$realfield} }, $data;
+        if (lc $ea eq 'none') {
+            $clause{$realfield} = [ $data ];
+        } else {
+            push @{ $clause{$realfield} }, $data;
+        }
     }
     return \%clause;
 }
     }
     return \%clause;
 }
index d0e6456..1f1bab1 100755 (executable)
@@ -512,7 +512,7 @@ sub Attachments {
     $self->{'attachments'} = RT::Attachments->new( $self->CurrentUser );
 
     unless ( $self->CurrentUserCanSee ) {
     $self->{'attachments'} = RT::Attachments->new( $self->CurrentUser );
 
     unless ( $self->CurrentUserCanSee ) {
-        $self->{'attachments'}->Limit(FIELD => 'id', VALUE => '0');
+        $self->{'attachments'}->Limit(FIELD => 'id', VALUE => '0', SUBCLAUSE => 'acl');
         return $self->{'attachments'};
     }
 
         return $self->{'attachments'};
     }
 
@@ -714,6 +714,7 @@ sub BriefDescription {
 
         if ( $self->Field ) {
             my $cf = RT::CustomField->new( $self->CurrentUser );
 
         if ( $self->Field ) {
             my $cf = RT::CustomField->new( $self->CurrentUser );
+            $cf->SetContextObject( $self->Object );
             $cf->Load( $self->Field );
             $field = $cf->Name();
             $field = $self->loc('a custom field') if !defined($field);
             $cf->Load( $self->Field );
             $field = $cf->Name();
             $field = $self->loc('a custom field') if !defined($field);
@@ -1072,14 +1073,8 @@ sub CurrentUserCanSee {
         $cf->Load( $cf_id );
         return 0 unless $cf->CurrentUserHasRight('SeeCustomField');
     }
         $cf->Load( $cf_id );
         return 0 unless $cf->CurrentUserHasRight('SeeCustomField');
     }
-    #if they ain't got rights to see, don't let em
-    elsif ( $self->__Value('ObjectType') eq "RT::Ticket" ) {
-        unless ( $self->CurrentUserHasRight('ShowTicket') ) {
-            return 0;
-        }
-    }
-
-    return 1;
+    # Defer to the object in question
+    return $self->Object->CurrentUserCanSee("Transaction");
 }
 
 
 }
 
 
@@ -1103,7 +1098,7 @@ sub OldValue {
         return $Object->Content;
     }
     else {
         return $Object->Content;
     }
     else {
-        return $self->__Value('OldValue');
+        return $self->_Value('OldValue');
     }
 }
 
     }
 }
 
@@ -1117,7 +1112,7 @@ sub NewValue {
         return $Object->Content;
     }
     else {
         return $Object->Content;
     }
     else {
-        return $self->__Value('NewValue');
+        return $self->_Value('NewValue');
     }
 }
 
     }
 }
 
@@ -1207,6 +1202,7 @@ sub CustomFieldValues {
         #      do we want to cover this situation somehow here?
         unless ( defined $field && $field =~ /^\d+$/o ) {
             my $CFs = RT::CustomFields->new( $self->CurrentUser );
         #      do we want to cover this situation somehow here?
         unless ( defined $field && $field =~ /^\d+$/o ) {
             my $CFs = RT::CustomFields->new( $self->CurrentUser );
+            $CFs->SetContextObject( $self->Object );
             $CFs->Limit( FIELD => 'Name', VALUE => $field );
             $CFs->LimitToLookupType($self->CustomFieldLookupType);
             $CFs->LimitToGlobalOrObjectId($self->Object->QueueObj->id);
             $CFs->Limit( FIELD => 'Name', VALUE => $field );
             $CFs->LimitToLookupType($self->CustomFieldLookupType);
             $CFs->LimitToGlobalOrObjectId($self->Object->QueueObj->id);
index 4af1cb0..fce0459 100644 (file)
@@ -130,7 +130,7 @@ sub FromURI {
     # Special case: integers passed in as URIs must be ticket ids
     if ($uri =~ /^(\d+)$/) {
        $scheme = "fsck.com-rt";
     # Special case: integers passed in as URIs must be ticket ids
     if ($uri =~ /^(\d+)$/) {
        $scheme = "fsck.com-rt";
-    } elsif ($uri =~ /^((?:\w|\.|-)+?):/) {
+    } elsif ($uri =~ /^((?!javascript|data)(?:\w|\.|-)+?):/i) {
        $scheme = $1;
     }
     else {
        $scheme = $1;
     }
     else {
index 503434a..0c09b7c 100644 (file)
@@ -188,7 +188,7 @@ Otherwise, return its URI
 sub HREF {
     my $self = shift;
     if ($self->IsLocal && $self->Object) {
 sub HREF {
     my $self = shift;
     if ($self->IsLocal && $self->Object) {
-        return ( RT->Config->Get('WebURL') . "/Articles/Article/Display.html?id=".$self->Object->Id);
+        return ( RT->Config->Get('WebURL') . "Articles/Article/Display.html?id=".$self->Object->Id);
     }   
     else {
         return ($self->URI);
     }   
     else {
         return ($self->URI);
index 2a14cd1..9b4a826 100755 (executable)
@@ -1206,6 +1206,37 @@ sub HasRight {
     return $self->PrincipalObj->HasRight(@_);
 }
 
     return $self->PrincipalObj->HasRight(@_);
 }
 
+=head2 CurrentUserCanSee [FIELD]
+
+Returns true if the current user can see the user, based on if it is
+public, ourself, or we have AdminUsers
+
+=cut
+
+sub CurrentUserCanSee {
+    my $self = shift;
+    my ($what) = @_;
+
+    # If it's public, fine.  Note that $what may be "transaction", which
+    # doesn't have an Accessible value, and thus falls through below.
+    if ( $self->_Accessible( $what, 'public' ) ) {
+        return 1;
+    }
+
+    # Users can see their own properties
+    elsif ( defined($self->Id) && $self->CurrentUser->Id == $self->Id ) {
+        return 1;
+    }
+
+    # If the user has the admin users right, that's also enough
+    elsif ( $self->CurrentUser->HasRight( Right => 'AdminUsers', Object => $RT::System) ) {
+        return 1;
+    }
+    else {
+        return 0;
+    }
+}
+
 =head2 CurrentUserCanModify RIGHT
 
 If the user has rights for this object, either because
 =head2 CurrentUserCanModify RIGHT
 
 If the user has rights for this object, either because
@@ -1334,12 +1365,13 @@ sub Stylesheet {
 
     my $style = RT->Config->Get('WebDefaultStylesheet', $self->CurrentUser);
 
 
     my $style = RT->Config->Get('WebDefaultStylesheet', $self->CurrentUser);
 
+    if (RT::Interface::Web->ComponentPathIsSafe($style)) {
+        my @css_paths = map { $_ . '/NoAuth/css' } RT::Interface::Web->ComponentRoots;
 
 
-    my @css_paths = map { $_ . '/NoAuth/css' } RT::Interface::Web->ComponentRoots;
-
-    for my $css_path (@css_paths) {
-        if (-d "$css_path/$style") {
-            return $style
+        for my $css_path (@css_paths) {
+            if (-d "$css_path/$style") {
+                return $style
+            }
         }
     }
 
         }
     }
 
@@ -1409,6 +1441,12 @@ sub WatchedQueues {
                             FIELD => 'MemberId',
                             VALUE => $self->PrincipalId,
                           );
                             FIELD => 'MemberId',
                             VALUE => $self->PrincipalId,
                           );
+    $watched_queues->Limit(
+                            ALIAS => $queues_alias,
+                            FIELD => 'Disabled',
+                            VALUE => 0,
+                          );
+
 
     $RT::Logger->debug("WatchedQueues got " . $watched_queues->Count . " queues");
 
 
     $RT::Logger->debug("WatchedQueues got " . $watched_queues->Count . " queues");
 
@@ -1447,7 +1485,9 @@ sub _Set {
     if ( $ret == 0 ) { return ( 0, $msg ); }
 
     if ( $args{'RecordTransaction'} == 1 ) {
     if ( $ret == 0 ) { return ( 0, $msg ); }
 
     if ( $args{'RecordTransaction'} == 1 ) {
-
+        if ($args{'Field'} eq "Password") {
+            $args{'Value'} = $Old = '********';
+        }
         my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
                                                Type => $args{'TransactionType'},
                                                Field     => $args{'Field'},
         my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
                                                Type => $args{'TransactionType'},
                                                Field     => $args{'Field'},
@@ -1473,25 +1513,9 @@ sub _Value {
     my $self  = shift;
     my $field = shift;
 
     my $self  = shift;
     my $field = shift;
 
-    #if the field is public, return it.
-    if ( $self->_Accessible( $field, 'public' ) ) {
-        return ( $self->SUPER::_Value($field) );
-
-    }
-
-    #If the user wants to see their own values, let them
-    # TODO figure ouyt a better way to deal with this
-    elsif ( defined($self->Id) && $self->CurrentUser->Id == $self->Id ) {
-        return ( $self->SUPER::_Value($field) );
-    }
-
-    #If the user has the admin users right, return the field
-    elsif ( $self->CurrentUser->HasRight(Right =>'AdminUsers', Object => $RT::System) ) {
-        return ( $self->SUPER::_Value($field) );
-    } else {
-        return (undef);
-    }
-
+    # Defer to the abstraction above to know if the field can be read
+    return $self->SUPER::_Value($field) if $self->CurrentUserCanSee($field);
+    return undef;
 }
 
 =head2 FriendlyName
 }
 
 =head2 FriendlyName
index f3b1b5c..2784fc7 100755 (executable)
@@ -188,6 +188,9 @@ sub MemberOfGroup {
                  FIELD1 => 'id',
                  ALIAS2 => $groupalias,
                  FIELD2 => 'MemberId' );
                  FIELD1 => 'id',
                  ALIAS2 => $groupalias,
                  FIELD2 => 'MemberId' );
+    $self->Limit( ALIAS => $groupalias,
+                  FIELD => 'Disabled',
+                  VALUE => 0 );
 
     $self->Limit( ALIAS    => "$groupalias",
                   FIELD    => 'GroupId',
 
     $self->Limit( ALIAS    => "$groupalias",
                   FIELD    => 'GroupId',
@@ -266,6 +269,11 @@ sub _JoinGroupMembers
         ALIAS2 => $principals,
         FIELD2 => 'id'
     );
         ALIAS2 => $principals,
         FIELD2 => 'id'
     );
+    $self->Limit(
+        ALIAS => $group_members,
+        FIELD => 'Disabled',
+        VALUE => 0,
+    ) if $args{'IncludeSubgroupMembers'};
 
     return $group_members;
 }
 
     return $group_members;
 }
index b438202..45c3770 100644 (file)
@@ -91,6 +91,7 @@ if (grep { m/help/ } @ARGV) {
 
 require RT;
 RT->LoadConfig();
 
 require RT;
 RT->LoadConfig();
+RT->InitLogging();
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
index b438202..45c3770 100644 (file)
@@ -91,6 +91,7 @@ if (grep { m/help/ } @ARGV) {
 
 require RT;
 RT->LoadConfig();
 
 require RT;
 RT->LoadConfig();
+RT->InitLogging();
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
index c52116f..c0655db 100755 (executable)
@@ -74,7 +74,7 @@ should wipeout.
 
 =head2 --sqldump <filename>
 
 
 =head2 --sqldump <filename>
 
-Outputs INSERT queiries into file. This dump can be used to restore data
+Outputs INSERT queries into file. This dump can be used to restore data
 after wiping out.
 
 By default creates files
 after wiping out.
 
 By default creates files
index ebea86c..37ef32f 100644 (file)
@@ -189,6 +189,8 @@ File::ShareDir
 File::Spec 0.8
 HTML::Quoted
 HTML::Scrubber 0.08
 File::Spec 0.8
 HTML::Quoted
 HTML::Scrubber 0.08
+HTML::TreeBuilder
+HTML::FormatText
 Log::Dispatch 2.23
 Sys::Syslog 0.16
 Locale::Maketext 1.06
 Log::Dispatch 2.23
 Sys::Syslog 0.16
 Locale::Maketext 1.06
@@ -245,8 +247,6 @@ CGI::Emulate::PSGI
 .
 
 $deps{'MAILGATE'} = [ text_to_hash( << '.') ];
 .
 
 $deps{'MAILGATE'} = [ text_to_hash( << '.') ];
-HTML::TreeBuilder
-HTML::FormatText
 Getopt::Long
 LWP::UserAgent
 Pod::Usage
 Getopt::Long
 LWP::UserAgent
 Pod::Usage
@@ -288,7 +288,7 @@ Test::Builder 0.90 # needed for is_passing
 Test::MockTime
 Log::Dispatch::Perl
 Test::WWW::Mechanize::PSGI
 Test::MockTime
 Log::Dispatch::Perl
 Test::WWW::Mechanize::PSGI
-Plack::Middleware::Test::StashWarnings
+Plack::Middleware::Test::StashWarnings 0.06
 Test::LongString
 .
 
 Test::LongString
 .
 
@@ -339,6 +339,7 @@ Net::SMTP
 $deps{'DASHBOARDS'} = [ text_to_hash( << '.') ];
 HTML::RewriteAttributes 0.04
 MIME::Types
 $deps{'DASHBOARDS'} = [ text_to_hash( << '.') ];
 HTML::RewriteAttributes 0.04
 MIME::Types
+URI 1.59
 .
 
 $deps{'GRAPHVIZ'} = [ text_to_hash( << '.') ];
 .
 
 $deps{'GRAPHVIZ'} = [ text_to_hash( << '.') ];
index 78533c6..3386cd1 100755 (executable)
@@ -91,6 +91,7 @@ if (grep { m/help/ } @ARGV) {
 
 require RT;
 RT->LoadConfig();
 
 require RT;
 RT->LoadConfig();
+RT->InitLogging();
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
index b438202..45c3770 100644 (file)
@@ -91,6 +91,7 @@ if (grep { m/help/ } @ARGV) {
 
 require RT;
 RT->LoadConfig();
 
 require RT;
 RT->LoadConfig();
+RT->InitLogging();
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
index 96ddaf0..43ca956 100644 (file)
@@ -105,7 +105,7 @@ $topic
 % }
 % if ($Action) {
 % unless ($Action eq "Move" and grep {$_->getNodeValue->Id == $Modify} $Element->getAllChildren) {
 % }
 % if ($Action) {
 % unless ($Action eq "Move" and grep {$_->getNodeValue->Id == $Modify} $Element->getAllChildren) {
-<li><input type="submit" name="<%$Prefix%>-<%$topic eq "root" ? 0 : $topic->Id%>" value="<&|/l&><%$Action%> here</&>" /></li>
+<li><input type="submit" name="<%$Prefix%>-<%$topic eq "root" ? 0 : $topic->Id%>" value="<% $Action eq 'Move' ? loc('Move here') : loc('Add here') %>" /></li>
 % }
 % }
 </ul>
 % }
 % }
 </ul>
index 20c3e9c..4ed86b6 100644 (file)
 <div class="hints">
 <&|/l&>RT can make this custom field's values into hyperlinks to another service.</&>
 <&|/l&>Fill in this field with a URL.</&>
 <div class="hints">
 <&|/l&>RT can make this custom field's values into hyperlinks to another service.</&>
 <&|/l&>Fill in this field with a URL.</&>
-<&|/l, '<tt>__id__</tt>', '<tt>__CustomField__</tt>' &>RT will replace [_1] and [_2] with the record's id and the custom field's value, respectively.</&>
+<&|/l_unsafe, '<tt>__id__</tt>', '<tt>__CustomField__</tt>' &>RT will replace [_1] and [_2] with the record's id and the custom field's value, respectively.</&>
 </div></td></tr>
 
 <tr><td class="label"><&|/l&>Include page</&></td><td>
 </div></td></tr>
 
 <tr><td class="label"><&|/l&>Include page</&></td><td>
 <div class="hints">
 <&|/l&>RT can include content from another web service when showing this custom field.</&>
 <&|/l&>Fill in this field with a URL.</&>
 <div class="hints">
 <&|/l&>RT can include content from another web service when showing this custom field.</&>
 <&|/l&>Fill in this field with a URL.</&>
-<&|/l, '<tt>__id__</tt>', '<tt>__CustomField__</tt>' &>RT will replace [_1] and [_2] with the record's id and the custom field's value, respectively.</&>
+<&|/l_unsafe, '<tt>__id__</tt>', '<tt>__CustomField__</tt>' &>RT will replace [_1] and [_2] with the record's id and the custom field's value, respectively.</&>
 <i><&|/l&>Some browsers may only load content from the same domain as your RT server.</&></i>
 </div></td></tr>
 
 <i><&|/l&>Some browsers may only load content from the same domain as your RT server.</&></i>
 </div></td></tr>
 
index aa7b622..d9d9134 100755 (executable)
@@ -128,6 +128,7 @@ if ( $MoveCustomFieldDown ) { {
 if ( $UpdateCFs ) {
     foreach my $cf_id ( @AddCustomField ) {
         my $CF = RT::CustomField->new( $session{'CurrentUser'} );
 if ( $UpdateCFs ) {
     foreach my $cf_id ( @AddCustomField ) {
         my $CF = RT::CustomField->new( $session{'CurrentUser'} );
+        $CF->SetContextObject( $Object );
         $CF->Load( $cf_id );
         unless ( $CF->id ) {
             push @results, loc("Couldn't load CustomField #[_1]", $cf_id);
         $CF->Load( $cf_id );
         unless ( $CF->id ) {
             push @results, loc("Couldn't load CustomField #[_1]", $cf_id);
@@ -138,6 +139,7 @@ if ( $UpdateCFs ) {
     }
     foreach my $cf_id ( @RemoveCustomField ) {
         my $CF = RT::CustomField->new( $session{'CurrentUser'} );
     }
     foreach my $cf_id ( @RemoveCustomField ) {
         my $CF = RT::CustomField->new( $session{'CurrentUser'} );
+        $CF->SetContextObject( $Object );
         $CF->Load( $cf_id );
         unless ( $CF->id ) {
             push @results, loc("Couldn't load CustomField #[_1]", $cf_id);
         $CF->Load( $cf_id );
         unless ( $CF->id ) {
             push @results, loc("Couldn't load CustomField #[_1]", $cf_id);
@@ -153,6 +155,7 @@ $m->callback(CallbackName => 'UpdateExtraFields', Results => \@results, Object =
 my $applied_cfs = RT::CustomFields->new( $session{'CurrentUser'} );
 $applied_cfs->LimitToLookupType($lookup);
 $applied_cfs->LimitToGlobalOrObjectId($id);
 my $applied_cfs = RT::CustomFields->new( $session{'CurrentUser'} );
 $applied_cfs->LimitToLookupType($lookup);
 $applied_cfs->LimitToGlobalOrObjectId($id);
+$applied_cfs->SetContextObject( $Object );
 $applied_cfs->ApplySortOrder;
 
 my $not_applied_cfs = RT::CustomFields->new( $session{'CurrentUser'} );
 $applied_cfs->ApplySortOrder;
 
 my $not_applied_cfs = RT::CustomFields->new( $session{'CurrentUser'} );
index e5b9908..e673593 100644 (file)
@@ -110,13 +110,13 @@ for my $category (@$Principals) {
                id="AddPrincipalForRights-<% lc $AddPrincipal %>" />
         <script type="text/javascript">
         jQuery(function() {
                id="AddPrincipalForRights-<% lc $AddPrincipal %>" />
         <script type="text/javascript">
         jQuery(function() {
-            jQuery("#AddPrincipalForRights-<% lc $AddPrincipal %>").keyup(function(){
+            jQuery("#AddPrincipalForRights-"+<% lc $AddPrincipal |n,j%>).keyup(function(){
                 toggle_addprincipal_validity(this, true);
             });
 
 % if (lc $AddPrincipal eq 'group') {
                 toggle_addprincipal_validity(this, true);
             });
 
 % if (lc $AddPrincipal eq 'group') {
-            jQuery("#AddPrincipalForRights-<% lc $AddPrincipal %>").autocomplete({
-                source: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Groups",
+            jQuery("#AddPrincipalForRights-"+<% lc $AddPrincipal |n,j%>).autocomplete({
+                source: <% RT->Config->Get('WebPath') |n,j%>+"/Helpers/Autocomplete/Groups",
                 select: addprincipal_onselect,
                 change: addprincipal_onchange
             });
                 select: addprincipal_onselect,
                 change: addprincipal_onchange
             });
index d5e75c5..821ed57 100644 (file)
@@ -47,6 +47,6 @@
 %# END BPS TAGGED BLOCK }}}
 <div id="rt-portal">
 <&| /Widgets/TitleBox, title => 'RT Portal' &>
 %# END BPS TAGGED BLOCK }}}
 <div id="rt-portal">
 <&| /Widgets/TitleBox, title => 'RT Portal' &>
-<iframe src="http://bestpractical.com/rt/integration/news?utm_source=rt&utm_medium=iframe&utm_campaign=<%$RT::VERSION%>"></iframe>
+<iframe src="https://bestpractical.com/rt/integration/news?utm_source=rt&utm_medium=iframe&utm_campaign=<%$RT::VERSION%>"></iframe>
 </&>
 </div>
 </&>
 </div>
index f386ba5..8778dae 100755 (executable)
@@ -50,8 +50,8 @@
 <input type="text" value="" name="<% $Name %>Users" id="<% $Name %>Users" /><br />
 <script type="text/javascript">
 jQuery(function(){
 <input type="text" value="" name="<% $Name %>Users" id="<% $Name %>Users" /><br />
 <script type="text/javascript">
 jQuery(function(){
-    jQuery("#<% $Name %>Users").autocomplete({
-        source: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Users?return=Name;privileged=1;exclude=<% $user_ids |u %>",
+    jQuery("#"+<% $Name |n,j%>+"Users").autocomplete({
+        source: <% RT->Config->Get('WebPath') |n,j%>+"/Helpers/Autocomplete/Users?return=Name;privileged=1;exclude="+<% $user_ids |n,u,j %>,
         // Auto-submit once a user is chosen
         select: function( event, ui ) {
             jQuery(event.target).val(ui.item.value);
         // Auto-submit once a user is chosen
         select: function( event, ui ) {
             jQuery(event.target).val(ui.item.value);
@@ -67,8 +67,8 @@ jQuery(function(){
 <input type="text" value="" name="<% $Name %>Groups" id="<% $Name %>Groups" /><br />
 <script type="text/javascript">
 jQuery(function(){
 <input type="text" value="" name="<% $Name %>Groups" id="<% $Name %>Groups" /><br />
 <script type="text/javascript">
 jQuery(function(){
-    jQuery("#<% $Name %>Groups").autocomplete({
-        source: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Groups?exclude=<% $group_ids |u %>",
+    jQuery("#"+<% $Name |n,j%>+"Groups").autocomplete({
+        source: <% RT->Config->Get('WebPath') |n,j%>+"/Helpers/Autocomplete/Groups?exclude="+<% $group_ids |n,u,j %>,
         // Auto-submit once a user is chosen
         select: function( event, ui ) {
             jQuery(event.target).val(ui.item.value);
         // Auto-submit once a user is chosen
         select: function( event, ui ) {
             jQuery(event.target).val(ui.item.value);
index bd07b73..ef7395f 100755 (executable)
@@ -57,7 +57,7 @@
 <script type="text/javascript">
 jQuery(function(){
     jQuery("#autocomplete-GroupString").autocomplete({
 <script type="text/javascript">
 jQuery(function(){
     jQuery("#autocomplete-GroupString").autocomplete({
-        source: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Groups",
+        source: <% RT->Config->Get('WebPath') |n,j%>+"/Helpers/Autocomplete/Groups",
         // Auto-submit once a group is chosen
         select: function( event, ui ) {
             jQuery(event.target).val(ui.item.value);
         // Auto-submit once a group is chosen
         select: function( event, ui ) {
             jQuery(event.target).val(ui.item.value);
index 2fe2d5a..dbc6fc5 100644 (file)
@@ -79,7 +79,7 @@ unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'Super
 
           <li>
               <tt><% $request->{Path} %></tt> - <i><&|/l, sprintf('%.4f', $seconds) &>[_1]s</&></i>
 
           <li>
               <tt><% $request->{Path} %></tt> - <i><&|/l, sprintf('%.4f', $seconds) &>[_1]s</&></i>
-              <a href="#" onclick="return hideshow('queries-<%$r%>');"><&|/l, $count &>Toggle [quant,_1,query,queries]</&></a>
+              <a href="#" onclick="return hideshow(<% "queries-$r" |n,j%>);"><&|/l, $count &>Toggle [quant,_1,query,queries]</&></a>
               <table id="queries-<%$r%>" class="tablesorter hidden">
                   <thead>
                       <tr>
               <table id="queries-<%$r%>" class="tablesorter hidden">
                   <thead>
                       <tr>
@@ -115,7 +115,7 @@ unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'Super
                                        <br><tt>[<% join(", ", @$b) %>]</tt>
 %                                  }
 %                              }
                                        <br><tt>[<% join(", ", @$b) %>]</tt>
 %                                  }
 %                              }
-                               <a class="query-stacktrace-toggle" href="#" onclick="return hideshow('trace-<%$r%>-<%$s%>');"><&|/l &>Toggle stack trace</&></a>
+                               <a class="query-stacktrace-toggle" href="#" onclick="return hideshow(<% "trace-$r-$s" |n,j%>);"><&|/l &>Toggle stack trace</&></a>
                                <pre id="trace-<%$r%>-<%$s%>" class="hidden"><% $trace %></pre>
                            </td>
                        </tr>
                                <pre id="trace-<%$r%>-<%$s%>" class="hidden"><% $trace %></pre>
                            </td>
                        </tr>
index 8b84cf4..0d24fa0 100644 (file)
@@ -48,9 +48,6 @@
 <%ATTR>
 AutoFlush => 0
 </%ATTR>
 <%ATTR>
 AutoFlush => 0
 </%ATTR>
-<%FLAGS>
-inherit => undef
-</%FLAGS>
 <%INIT>
 my $arg = $m->dhandler_arg;
 $m->abort(404) if $arg =~ m{\.\.|/|\\};
 <%INIT>
 my $arg = $m->dhandler_arg;
 $m->abort(404) if $arg =~ m{\.\.|/|\\};
@@ -64,5 +61,5 @@ my $buf;
 while( read $fh, $buf, 1024*1024 ) {
     $m->out($buf);
 }
 while( read $fh, $buf, 1024*1024 ) {
     $m->out($buf);
 }
-return 0;
+$m->abort;
 </%INIT>
 </%INIT>
index ce93411..ae3b96e 100644 (file)
@@ -52,5 +52,5 @@ $Path => ''
 <& /Elements/Tabs &>
 <div class="error">
 % my $path_tag = q{<span class="file-path">} . $m->interp->apply_escapes($Path, 'h') . q{</span>};
 <& /Elements/Tabs &>
 <div class="error">
 % my $path_tag = q{<span class="file-path">} . $m->interp->apply_escapes($Path, 'h') . q{</span>};
-<&|/l, $path_tag &>Shredder needs a directory to write dumps to. Please ensure that the directory [_1] exists and that it is writable by your web server.</&>
+<&|/l_unsafe, $path_tag &>Shredder needs a directory to write dumps to. Please ensure that the directory [_1] exists and that it is writable by your web server.</&>
 </div>
 </div>
index 11b876b..0da910d 100644 (file)
@@ -49,6 +49,6 @@
 $Object => undef
 </%ARGS>
 % my $name = (defined $Object->Filename and length $Object->Filename) ? $Object->Filename : loc("(no value)");
 $Object => undef
 </%ARGS>
 % my $name = (defined $Object->Filename and length $Object->Filename) ? $Object->Filename : loc("(no value)");
-<a href="<% RT->Config->Get('WebURL') %>/Ticket/Attachment/<% $Object->TransactionId %>/<% $Object->id %>/">
+<a href="<% RT->Config->Get('WebPath') %>/Ticket/Attachment/<% $Object->TransactionId %>/<% $Object->id %>/">
 <% loc('Attachment') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Filename') %>: <% $name %>)
 </a>
 <% loc('Attachment') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Filename') %>: <% $name %>)
 </a>
index 13547ad..35f1aa8 100644 (file)
@@ -48,6 +48,6 @@
 <%ARGS>
 $Object => undef
 </%ARGS>
 <%ARGS>
 $Object => undef
 </%ARGS>
-<a href="<% RT->Config->Get('WebURL') %>/Ticket/Display.html?id=<% $Object->id %>">
+<a href="<% RT->Config->Get('WebPath') %>/Ticket/Display.html?id=<% $Object->id %>">
 <% loc('Ticket') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Subject') %>: <% substr($Object->Subject, 0, 30) %>...)
 </a>
 <% loc('Ticket') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Subject') %>: <% substr($Object->Subject, 0, 30) %>...)
 </a>
index f77169a..d7627eb 100644 (file)
@@ -48,6 +48,6 @@
 <%ARGS>
 $Object => undef
 </%ARGS>
 <%ARGS>
 $Object => undef
 </%ARGS>
-<a href="<% RT->Config->Get('WebURL') %>/Admin/Users/Modify.html?id=<% $Object->id %>">
+<a href="<% RT->Config->Get('WebPath') %>/Admin/Users/Modify.html?id=<% $Object->id %>">
 <% loc('User') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Name') %>: <% $Object->Name %>)
 </a>
 <% loc('User') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Name') %>: <% $Object->Name %>)
 </a>
index b14f936..05dde17 100755 (executable)
 % if ($UserObj->id) {
 <& /Elements/EditCustomField, %ARGS, Object => $UserObj, CustomField => $CF &>
 % } else {
 % if ($UserObj->id) {
 <& /Elements/EditCustomField, %ARGS, Object => $UserObj, CustomField => $CF &>
 % } else {
-<& /Elements/EditCustomField, %ARGS, NamePrefix => 'Object-RT::User-new-CustomField-', CustomField => $CF &>
+<& /Elements/EditCustomField, %ARGS, NamePrefix => 'Object-RT::User--CustomField-', CustomField => $CF &>
 % }
 </td></tr>
 % }
 % }
 </td></tr>
 % }
@@ -282,12 +282,6 @@ else {
 
        if ($val) {
                push @results, $msg;
 
        if ($val) {
                push @results, $msg;
-        foreach my $key ( keys %ARGS) {
-            # Convert custom fields on the "new" object to custom fields on the one we've just created
-            if ($key =~ /^Object-RT::User-new-CustomField-(.*)$/) {
-            $ARGS{'Object-RT::User-'.$val.'-CustomField-'.$1} = delete $ARGS{$key};
-            }
-        }
         push @results, ProcessObjectCustomFieldUpdates( ARGSRef => \%ARGS, Object => $UserObj );
        } else {
                push @results, loc('User could not be created: [_1]', $msg);
         push @results, ProcessObjectCustomFieldUpdates( ARGSRef => \%ARGS, Object => $UserObj );
        } else {
                push @results, loc('User could not be created: [_1]', $msg);
index a1e3fac..adcfeb5 100755 (executable)
@@ -62,7 +62,7 @@
 <script type="text/javascript">
 jQuery(function(){
     jQuery("#autocomplete-UserString").autocomplete({
 <script type="text/javascript">
 jQuery(function(){
     jQuery("#autocomplete-UserString").autocomplete({
-        source: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Users?return=Name",
+        source: <% RT->Config->Get('WebPath') |n,j%>+"/Helpers/Autocomplete/Users?return=Name",
         // Auto-submit once a user is chosen
         select: function( event, ui ) {
             jQuery(event.target).val(ui.item.value);
         // Auto-submit once a user is chosen
         select: function( event, ui ) {
             jQuery(event.target).val(ui.item.value);
index 75ad5e1..d2061da 100755 (executable)
@@ -63,9 +63,9 @@
 <input type="checkbox" class="checkbox" value="1" name="ShowRejected" <% defined($ARGS{'ShowRejected'}) && $ARGS{'ShowRejected'} && qq[checked="checked"] |n%> /> <&|/l&>Show denied requests</&><br />
 <input type="checkbox" class="checkbox" value="1" name="ShowDependent" <% defined($ARGS{'ShowDependent'}) && $ARGS{'ShowDependent'} && qq[checked="checked"] |n%> /> <&|/l&>Show requests awaiting other approvals</&><br />
 
 <input type="checkbox" class="checkbox" value="1" name="ShowRejected" <% defined($ARGS{'ShowRejected'}) && $ARGS{'ShowRejected'} && qq[checked="checked"] |n%> /> <&|/l&>Show denied requests</&><br />
 <input type="checkbox" class="checkbox" value="1" name="ShowDependent" <% defined($ARGS{'ShowDependent'}) && $ARGS{'ShowDependent'} && qq[checked="checked"] |n%> /> <&|/l&>Show requests awaiting other approvals</&><br />
 
-<&|/l, qq{<input size='15' class="ui-datepicker" value='}.($created_before->Unix > 0 &&$created_before->ISO(Timezone => 'user'))."' name='CreatedBefore' id='CreatedBefore' />"&>Only show approvals for requests created before [_1]</&><br />
+<&|/l_unsafe, qq{<input size='15' class="ui-datepicker" value='}.($created_before->Unix > 0 &&$created_before->ISO(Timezone => 'user'))."' name='CreatedBefore' id='CreatedBefore' />"&>Only show approvals for requests created before [_1]</&><br />
 
 
-<&|/l, qq{<input size='15' class="ui-datepicker" value='}.( $created_after->Unix >0 && $created_after->ISO(Timezone => 'user'))."' name='CreatedAfter' id='CreatedAfter' />"&>Only show approvals for requests created after [_1]</&>
+<&|/l_unsafe, qq{<input size='15' class="ui-datepicker" value='}.( $created_after->Unix >0 && $created_after->ISO(Timezone => 'user'))."' name='CreatedAfter' id='CreatedAfter' />"&>Only show approvals for requests created after [_1]</&>
 </&>
 
 <%init>
 </&>
 
 <%init>
index 756aa2c..d14c330 100644 (file)
@@ -157,6 +157,7 @@ elsif ( $id eq 'new' ) {
             my $cfid = $1; 
         
             my $cf = RT::CustomField->new( $session{'CurrentUser'} );
             my $cfid = $1; 
         
             my $cf = RT::CustomField->new( $session{'CurrentUser'} );
+            $cf->SetContextObject( $ArticleObj );
             $cf->Load( $cfid );
             unless ( $cf->id ) {
                 $RT::Logger->error( "Couldn't load custom field #". $cfid );
             $cf->Load( $cfid );
             unless ( $cf->id ) {
                 $RT::Logger->error( "Couldn't load custom field #". $cfid );
index 807360b..82e9071 100644 (file)
 %# END BPS TAGGED BLOCK }}}
 <input type="hidden" name="EditTopics" value="1" />
 <select multiple size="10" name="Topics">
 %# END BPS TAGGED BLOCK }}}
 <input type="hidden" name="EditTopics" value="1" />
 <select multiple size="10" name="Topics">
-<%perl>
-if (@Classes) {
-  $m->print("<optgroup label=\"Current classes (".join (' ',map {$_->Name} @Classes).")\">")
-    unless $OnlyThisClass;
-  $inTree->traverse(sub {
-    my $tree = shift;
-    my $topic = $tree->getNodeValue;
-    $m->print("<option value=\"".$topic->Id."\""
-      .(exists $topics{$topic->Id} ? " selected" : "").">"
-      .("&nbsp;" x ($tree->getDepth*5)).($topic->Name || loc("(no name)"))."</option>\n");
-  });
-}
-unless ($OnlyThisClass) {
-  my $class = $Classes[-1]->Id;
-  $otherTree->traverse(sub {
-    my $tree = shift;
-    my $topic = $tree->getNodeValue;
-    unless ($topic->ObjectId == $class) {
-      $class = $topic->ObjectId;
-      $m->print("</optgroup>\n");
-      my $c = RT::Class->new($session{'CurrentUser'});
-      $c->Load($topic->ObjectId);
-      $m->print("<optgroup label=\"".$c->Name."\">\n");
-    }
-    $m->print("<option value=\"".$topic->Id."\""
-      .(exists $topics{$topic->Id} ? " selected" : "").">"
-      .("&nbsp;" x ($tree->getDepth*5)).($topic->Name || loc("(no name)"))."</option>\n");
-  });
-</%perl>
+% if (@Classes) {
+%   unless ($OnlyThisClass) {
+<optgroup label="Current classes (<% join(" ", map {$_->Name} @Classes) %>)">
+%   }
+%   $inTree->traverse(sub {
+%     my $tree = shift;
+%     my $topic = $tree->getNodeValue;
+<option value="<% $topic->Id %>" <% exists $topics{$topic->Id} ? "selected" : "" %> >\
+<% "&nbsp;" x ($tree->getDepth*5) |n %><% $topic->Name || loc("(no name)") %></option>
+%   });
+% }
+% unless ($OnlyThisClass) {
+%   my $class = $Classes[-1]->Id;
+%   $otherTree->traverse(sub {
+%     my $tree = shift;
+%     my $topic = $tree->getNodeValue;
+%     unless ($topic->ObjectId == $class) {
+%       $class = $topic->ObjectId;
+</optgroup>
+%       my $c = RT::Class->new($session{'CurrentUser'});
+%       $c->Load($topic->ObjectId);
+<optgroup label="<% $c->Name %>">
+%     }
+<option value="<% $topic->Id %>" <% exists $topics{$topic->Id} ? "selected" : "" %> >\
+<% "&nbsp;" x ($tree->getDepth*5) |n %><% $topic->Name || loc("(no name)") %></option>
+%   });
 </optgroup>
 % }
 </select>
 </optgroup>
 % }
 </select>
index adf23fc..f3618fe 100644 (file)
@@ -54,7 +54,7 @@
 % my $Classes = RT::Classes->new($session{'CurrentUser'});
 % $Classes->LimitToEnabled();
 % while (my $Class = $Classes->Next) {
 % my $Classes = RT::Classes->new($session{'CurrentUser'});
 % $Classes->LimitToEnabled();
 % while (my $Class = $Classes->Next) {
-<li><a href="ExtractIntoTopic.html?Ticket=<%$Ticket%>&Class=<%$Class->Id%>" onclick="document.getElementById('topics-<% $Class->Id %>').style.display = (document.getElementById('topics-<% $Class->Id %>').style.display == 'block') ? 'none' : 'block'; return false;"><%$Class->Name%></a>: 
+<li><a href="ExtractIntoTopic.html?Ticket=<%$Ticket%>&Class=<%$Class->Id%>" onclick="document.getElementById('topics-'+<% $Class->Id |n,j%>).style.display = (document.getElementById('topics-'+<% $Class->Id |n,j%>).style.display == 'block') ? 'none' : 'block'; return false;"><%$Class->Name%></a>: 
 <%$Class->Description%>
 <div id="topics-<%$Class->Id%>" style="display: none">
 <form action="ExtractFromTicket.html">
 <%$Class->Description%>
 <div id="topics-<%$Class->Id%>" style="display: none">
 <form action="ExtractFromTicket.html">
index 9a07c08..5187315 100644 (file)
@@ -48,7 +48,6 @@
 <& /Elements/Header, Title => loc('Browse by topic') &>
 <& /Elements/Tabs &>
 
 <& /Elements/Header, Title => loc('Browse by topic') &>
 <& /Elements/Tabs &>
 
-<& /Elements/ListActions, actions => \@Actions &>
 <a href="Topics.html"><&|/l&>All topics</&></a>
 % if (defined $class) {
 &gt; <a href="Topics.html?class=<%$currclass_id%>"><% $currclass_name %></a>
 <a href="Topics.html"><&|/l&>All topics</&></a>
 % if (defined $class) {
 &gt; <a href="Topics.html?class=<%$currclass_id%>"><% $currclass_name %></a>
 % }
 <br />
 <h1><&|/l&>Browse by topic</&></h1>
 % }
 <br />
 <h1><&|/l&>Browse by topic</&></h1>
-<%perl>
-if (defined $class) {
-   $m->print('<h2>'.'<a href="'.
-   RT->Config->Get('WebPath')."/Articles/Topics.html?class=" . $currclass_id
-   .'">'.$currclass_name."</a></h2>\n");
-   ProduceTree(\@Actions, $currclass, $currclass_id, $currclass_name, 0, $id);
-} else {
-    $m->print("<ul>\n");
-    while (my $c = $Classes->Next) {
-        $m->print('<li><h2>'.'<a href="'.
-        RT->Config->Get('WebPath')."/Articles/Topics.html?class=" . $c->Id
-        .'">'.$c->Name."</a></h2>\n");
-        $m->print("\n</li>\n");
-    }
-    $m->print(qq|<li><h2><a href="|.RT->Config->Get('WebPath').qq|/Articles/Topics.html?class=0">|.loc('Global Topics').qq|</a></h2></li>\n|);
-    $m->print("</ul>\n");
-}
-</%perl>
+% if (defined $class) {
+<h2><a href="<% RT->Config->Get('WebPath') %>/Articles/Topics.html?class=<% $currclass_id %>"><% $currclass_name %></a></h2>
+%     my $rtopic = RT::Topic->new( $session{'CurrentUser'} );
+%     $rtopic->Load($id);
+%     unless ( $rtopic->Id()
+%         && $rtopic->ObjectId() == $currclass->Id )
+%     {
+%         # Show all of them
+%         $ProduceTree->( 0 );
+%     } else {
+%         my @showtopics = ( $rtopic );
+%         my $parent = $rtopic->ParentObj;
+%         while ( $parent->Id ) {
+%             unshift @showtopics, $parent;
+%             $parent = $parent->ParentObj;
+%         }
+%         # List the topics.
+%         for my $t ( @showtopics ) {
+<ul><li><& /Articles/Elements/ShowTopicLink, Topic => $t, Class => $currclass_id &>
+%             $ProduceTree->( $id ) if $t->Id == $id;
+%         }
+%         for ( @showtopics ) {
+              </li></ul>
+%         }
+%     }
+% } else {
+<ul>
+%     while (my $c = $Classes->Next) {
+<li><h2><a href="<% RT->Config->Get('WebPath') %>/Articles/Topics.html?class=<% $c->Id %>"><% $c->Name %></a></h2></li>
+%     }
+<li><h2><a href="<% RT->Config->Get('WebPath') %>/Articles/Topics.html?class=0"><&|/l&>Global Topics</&></a></h2></li>
+</ul>
+% }
 
 <br />
 
 <br />
-<%perl>
-my @articles;
-if ($id or $showall) {
-    my $Articles = RT::ObjectTopics->new($session{'CurrentUser'});
-    $Articles->Limit(FIELD => 'ObjectType', VALUE => 'RT::Article');
-    if ($id) {
-        $Articles->Limit(FIELD => 'Topic', VALUE => $id, ENTRYAGGREGATOR => 'OR');
-        if ($showall) {
-            my $kids = $currtopic->Children;
-            while (my $k = $kids->Next) {
-                $Articles->Limit(FIELD => 'Topic', VALUE => $k->Id,
-                                 ENTRYAGGREGATOR => 'OR');
-            }
-        }
-    }
-    @articles = map {$a = RT::Article->new($session{'CurrentUser'}); $a->Load($_->ObjectId); $a} @{$Articles->ItemsArrayRef}
-} elsif ($class) {
-    my $Articles = RT::Articles->new($session{'CurrentUser'});
-    my $TopicsAlias = $Articles->Join(
-        TYPE   => 'left',
-        ALIAS1 => 'main',
-        FIELD1 => 'id',
-        TABLE2 => 'ObjectTopics',
-        FIELD2 => 'ObjectId',
-    );
-    $Articles->Limit(
-        LEFTJOIN => $TopicsAlias,
-        FIELD    => 'ObjectType',
-        VALUE    => 'RT::Article',
-    );
-    $Articles->Limit(
-        ALIAS      => $TopicsAlias,
-        FIELD      => 'Topic',
-        OPERATOR   => 'IS',
-        VALUE      => 'NULL',
-        QUOTEVALUE => 0,
-    );
-    $Articles->Limit(
-        FIELD      => 'Class',
-        OPERATOR   => '=',
-        VALUE      => $class,
-    );
-    @articles = @{$Articles->ItemsArrayRef};
-}
-</%perl>
 
 % if (@articles) {
 %   if ($id) {
 
 % if (@articles) {
 %   if ($id) {
@@ -139,7 +108,6 @@ if ($id or $showall) {
 % }
 
 <%init>
 % }
 
 <%init>
-my @Actions;
 my $Classes;
 my $currclass;
 my $currclass_id;
 my $Classes;
 my $currclass;
 my $currclass_id;
@@ -167,106 +135,65 @@ if ($id) {
     $currtopic->Load($id);
 }
 
     $currtopic->Load($id);
 }
 
-# A subroutine that iterates through topics and their children, producing
-# the necessary ul, li, and href links for the table of contents.  Thank
-# heaven for query caching.  The $restrict variable is used to display only
-# the branch of the hierarchy which contains that topic ID.
-
-sub ProduceTree {
-    my ( $Actions, $currclass, $currclass_id, $currclass_name, $parentid, $restrictid ) = @_;
-    $parentid = 0 unless $parentid;
-
-    # Deal with tree restriction, if any.
-    if ($restrictid) {
-        my $rtopic = RT::Topic->new( $session{'CurrentUser'} );
-        $rtopic->Load($restrictid);
-        unless ( $rtopic->Id()
-            && $rtopic->ObjectId() == $currclass_id )
-        {
-            push( @{$Actions},"Could not restrict view to topic $restrictid");
-
-            # Start over, without the restriction.
-            &ProduceTree( $Actions, $currclass, $currclass_id, $currclass_name, $parentid, undef );
-        } else {
-            my @showtopics;
-            push( @showtopics, $rtopic );
-            my $parent = $rtopic->ParentObj;
-            while ( $parent->Id ) {
-                push( @showtopics, $parent );
-                my $newparent = $parent->ParentObj;
-                $parent = $newparent;
-            }
-
-            # List the topics.
-            my $indents = @showtopics;
-            while ( my $t = pop @showtopics ) {
-                print "<ul>";
-                print &MakeLinks( $t, $currclass, $currclass_id, $currclass_name, $t->Children->Count );
-                if ( $t->Id == $restrictid ) {
-                    &ProduceTree( $Actions, $currclass, $currclass_id, $currclass_name, $restrictid, undef );
-                }
-            }
-            print "</ul>" x $indents;
-        }
-    } else {
-
-        # No restriction in place.  Build the entire tree.
-        my $topics = RT::Topics->new( $session{'CurrentUser'} );
-        $topics->LimitToObject($currclass);
-        $topics->LimitToKids($parentid);
-        $topics->OrderBy( FIELD => 'Name' );
-        print "<ul>" if $topics->Count;
-        while ( my $t = $topics->Next ) {
-            if ( $t->Children->Count ) {
-                print &MakeLinks( $t, $currclass, $currclass_id, $currclass_name, 1 );
-                &ProduceTree( $Actions, $currclass, $currclass_id, $currclass_name, $t->Id );
-            } else {
-                print &MakeLinks( $t, $currclass, $currclass_id, $currclass_name, 0 );
-            }
-        }
-        print "</ul>\n" if $topics->Count;
+my $ProduceTree;
+$ProduceTree = sub {
+    my ( $parentid ) = @_;
+    my $topics = RT::Topics->new( $session{'CurrentUser'} );
+    $topics->LimitToObject($currclass);
+    $topics->LimitToKids($parentid || 0);
+    $topics->OrderBy( FIELD => 'Name' );
+    return unless $topics->Count;
+    $m->out("<ul>");
+    while ( my $t = $topics->Next ) {
+        $m->out("<li>");
+        $m->comp("/Articles/Elements/ShowTopicLink",
+                 Topic => $t,
+                 Class => $currclass_id,
+             );
+        $ProduceTree->( $t->Id ) if $t->Children->Count;
+        $m->out("</li>");
     }
     }
-}
-
-sub MakeLinks {
-    my ( $topic, $currclass, $currclass_id, $currclass_name, $haschild ) = @_;
-    my $query;
-    my $output;
-
-    if ( ref($topic) eq 'RT::Topic' ) {
-
-        my $topic_info = $topic->Name() || loc("(no name)");
-        $topic_info .= ": " . $topic->Description() if $topic->Description;
-
-        if ($haschild) { # has topics below it
-            $query  = "Topics.html?id=" . $topic->Id . "&class=" . $currclass_id;
-            $output = qq(<li><a href="$query">$topic_info</a>);
-        } else {
-            $output = qq(<li>$topic_info);
-        }
+    $m->out("</ul>");
+};
 
 
-        my $Articles = RT::ObjectTopics->new( $session{'CurrentUser'} );
-        $Articles->Limit( FIELD => 'ObjectType', VALUE => 'RT::Article' );
-        $Articles->Limit( FIELD => 'Topic',      VALUE => $topic->Id );
-        if ( $Articles->Count ) {
-            my $article_text = " (" . loc( "[quant,_1,article]", $Articles->Count ) . ")";
-            my $query  = "Topics.html?id=" . $topic->Id . "&class=$currclass_id&showall=1";
-            $output .= qq(<a href="$query">$article_text</a>);
-        }
-
-        $output .= "</li>\n";
-
-    } else {
-
-        # This builds a link for the class specified, with no particular topic.
-        $query  = "Topics.html?class=" . $currclass_id;
-        $output = "<li><a href=\"$query\">" . $currclass_name . "</a>";
-        $output .= ": " . $currclass->Description if $currclass->Description;
+my @articles;
+if ($id) {
+    my $Articles = RT::ObjectTopics->new($session{'CurrentUser'});
+    $Articles->Limit(FIELD => 'ObjectType', VALUE => 'RT::Article');
+    $Articles->Limit(FIELD => 'Topic', VALUE => $id);
+    while (my $objtopic = $Articles->Next) {
+        my $a = RT::Article->new($session{'CurrentUser'});
+        $a->Load($objtopic->ObjectId);
+        push @articles, $a;
     }
     }
-
-    return $output;
+} elsif ($class) {
+    my $Articles = RT::Articles->new($session{'CurrentUser'});
+    my $TopicsAlias = $Articles->Join(
+        TYPE   => 'left',
+        ALIAS1 => 'main',
+        FIELD1 => 'id',
+        TABLE2 => 'ObjectTopics',
+        FIELD2 => 'ObjectId',
+    );
+    $Articles->Limit(
+        LEFTJOIN => $TopicsAlias,
+        FIELD    => 'ObjectType',
+        VALUE    => 'RT::Article',
+    );
+    $Articles->Limit(
+        ALIAS      => $TopicsAlias,
+        FIELD      => 'Topic',
+        OPERATOR   => 'IS',
+        VALUE      => 'NULL',
+        QUOTEVALUE => 0,
+    );
+    $Articles->Limit(
+        FIELD      => 'Class',
+        OPERATOR   => '=',
+        VALUE      => $class,
+    );
+    @articles = @{$Articles->ItemsArrayRef};
 }
 }
-
 </%init>
 
 <%args>
 </%init>
 
 <%args>
index 81d8bbb..20586f9 100644 (file)
@@ -129,11 +129,11 @@ foreach my $col ( @Format ) {
             if $OrderBy[0] && ($OrderBy[0] eq $attr or "$attr|$OrderBy[0]" =~ /^(Created|id)\|(Created|id)$/);
 
         $m->out(
             if $OrderBy[0] && ($OrderBy[0] eq $attr or "$attr|$OrderBy[0]" =~ /^(Created|id)\|(Created|id)$/);
 
         $m->out(
-            '<a href="' . $BaseURL
+            '<a href="' . $m->interp->apply_escapes($BaseURL
             . $m->comp( '/Elements/QueryString',
                 %$generic_query_args,
                 OrderBy => $attr, Order => $new_order
             . $m->comp( '/Elements/QueryString',
                 %$generic_query_args,
                 OrderBy => $attr, Order => $new_order
-            )
+            ), 'h')
             . '">'. loc($title) .'</a>'
         );
     }
             . '">'. loc($title) .'</a>'
         );
     }
index 0c816fe..402a272 100644 (file)
@@ -68,7 +68,7 @@ if ( $Rows ) {
 # collection is ordered or not
 if ( @OrderBy && ($AllowSorting || !$Collection->{'order_by'}) ) {
     if ( $OrderBy[0] =~ /\|/ ) {
 # collection is ordered or not
 if ( @OrderBy && ($AllowSorting || !$Collection->{'order_by'}) ) {
     if ( $OrderBy[0] =~ /\|/ ) {
-        @OrderBy = split /\|/, $OrderBy[0];
+        @OrderBy = grep length($_), split /\|/, $OrderBy[0];
         @Order = split /\|/,$Order[0];
     }
     $Collection->OrderByCols(
         @Order = split /\|/,$Order[0];
     }
     $Collection->OrderByCols(
index b1faa21..26c0823 100644 (file)
@@ -55,22 +55,24 @@ $URLParams => undef
 </%ARGS>
 
 <%INIT>
 </%ARGS>
 
 <%INIT>
+$BaseURL = $m->interp->apply_escapes($BaseURL, 'h');
+
 $m->out(qq{<div class="paging">});
 if ($Pages == 1) {
   $m->out(loc('Page 1 of 1'));
 }
 else{
 $m->out(loc('Page') . ' ');
 $m->out(qq{<div class="paging">});
 if ($Pages == 1) {
   $m->out(loc('Page 1 of 1'));
 }
 else{
 $m->out(loc('Page') . ' ');
-my $prev = $m->comp(
+my $prev = $m->interp->apply_escapes($m->comp(
                    '/Elements/QueryString',
                    %$URLParams,
                    Page    => ( $CurrentPage - 1 )
                    '/Elements/QueryString',
                    %$URLParams,
                    Page    => ( $CurrentPage - 1 )
-                  );
-my $next = $m->comp(
+                  ), 'h');
+my $next = $m->interp->apply_escapes($m->comp(
                    '/Elements/QueryString',
                    %$URLParams,
                    Page    => ( $CurrentPage + 1 )
                    '/Elements/QueryString',
                    %$URLParams,
                    Page    => ( $CurrentPage + 1 )
-                  );
+                  ), 'h');
 my %show;
 $show{1} = 1;
 $show{$_} = 1 for (($CurrentPage - 2)..($CurrentPage + 2));
 my %show;
 $show{1} = 1;
 $show{$_} = 1 for (($CurrentPage - 2)..($CurrentPage + 2));
@@ -81,7 +83,7 @@ for my $number ( 1 .. $Pages ) {
     if ( $show{$number} ) {
         $dots = undef;
         my $qs =
     if ( $show{$number} ) {
         $dots = undef;
         my $qs =
-          $m->comp( '/Elements/QueryString', %$URLParams, Page => $number );
+          $m->interp->apply_escapes($m->comp( '/Elements/QueryString', %$URLParams, Page => $number ), 'h');
        $m->out(qq{<span class="pagenum">});
         if ( $number == $CurrentPage ) {
             $m->out(qq{<span class="currentpage">$number</span> });
        $m->out(qq{<span class="pagenum">});
         if ( $number == $CurrentPage ) {
             $m->out(qq{<span class="currentpage">$number</span> });
index 745e57f..b9c3b4b 100644 (file)
@@ -120,14 +120,16 @@ my $COLUMN_MAP = {
             my $name = $_[1] || 'SelectedTickets';
             my $checked = $m->request_args->{ $name .'All' }? 'checked="checked"': '';
 
             my $name = $_[1] || 'SelectedTickets';
             my $checked = $m->request_args->{ $name .'All' }? 'checked="checked"': '';
 
-            return \qq{<input type="checkbox" name="${name}All" value="1" $checked
-                              onclick="setCheckbox(this.form, '$name', this.checked)" />};
+            return \qq{<input type="checkbox" name="}, $name, \qq{All" value="1" $checked
+                              onclick="setCheckbox(this.form, },
+                              $m->interp->apply_escapes($name,'j'),
+                              \qq{, this.checked)" />};
         },
         value => sub {
             my $id = $_[0]->id;
 
             my $name = $_[2] || 'SelectedTickets';
         },
         value => sub {
             my $id = $_[0]->id;
 
             my $name = $_[2] || 'SelectedTickets';
-            return \qq{<input type="checkbox" name="$name" value="$id" checked="checked" />}
+            return \qq{<input type="checkbox" name="}, $name, \qq{" value="$id" checked="checked" />}
                 if $m->request_args->{ $name . 'All'};
 
             my $arg = $m->request_args->{ $name };
                 if $m->request_args->{ $name . 'All'};
 
             my $arg = $m->request_args->{ $name };
@@ -138,7 +140,7 @@ my $COLUMN_MAP = {
             elsif ( $arg ) {
                 $checked = 'checked="checked"' if $arg == $id;
             }
             elsif ( $arg ) {
                 $checked = 'checked="checked"' if $arg == $id;
             }
-            return \qq{<input type="checkbox" name="$name" value="$id" $checked />}
+            return \qq{<input type="checkbox" name="}, $name, \qq{" value="$id" $checked />}
         },
     },
     RadioButton => {
         },
     },
     RadioButton => {
index 6e541db..6702abc 100755 (executable)
@@ -51,7 +51,7 @@
 % my $button_start = '<input type="submit" class="button" value="';
 % my $button_end = '" />';
 % my $queue_selector = $m->scomp('/Elements/SelectNewTicketQueue', OnChange => 'document.CreateTicketInQueue.submit()', SendTo => $SendTo );
 % my $button_start = '<input type="submit" class="button" value="';
 % my $button_end = '" />';
 % my $queue_selector = $m->scomp('/Elements/SelectNewTicketQueue', OnChange => 'document.CreateTicketInQueue.submit()', SendTo => $SendTo );
-<&|/l, $button_start, $button_end, $queue_selector &>[_1]New ticket in[_2]&nbsp;[_3]</&>
+<&|/l_unsafe, $button_start, $button_end, $queue_selector &>[_1]New ticket in[_2]&nbsp;[_3]</&>
 % $m->callback(CallbackName => 'BeforeFormEnd');
 </form>
 <%ARGS>
 % $m->callback(CallbackName => 'BeforeFormEnd');
 </form>
 <%ARGS>
index c7c8bfa..b74c484 100644 (file)
@@ -85,7 +85,7 @@ if ($MaxValues == 1 && $Values) {
 }
 # The "Magic" hidden input causes RT to know that we were trying to edit the field, even if 
 # we don't see a value later, since browsers aren't compelled to submit empty form fields
 }
 # The "Magic" hidden input causes RT to know that we were trying to edit the field, even if 
 # we don't see a value later, since browsers aren't compelled to submit empty form fields
-$m->out("\n".'<input type="hidden" class="hidden" name="'.$NamePrefix.$CustomField->Id.'-Values-Magic" value="1" />'."\n");
+$m->out("\n".'<input type="hidden" class="hidden" name="'.$m->interp->apply_escapes($NamePrefix, 'h').$CustomField->Id.'-Values-Magic" value="1" />'."\n");
 
 my $EditComponent = "EditCustomField$Type";
 $m->callback( %ARGS, CallbackName => 'EditComponentName', Name => \$EditComponent, CustomField => $CustomField, Object => $Object );
 
 my $EditComponent = "EditCustomField$Type";
 $m->callback( %ARGS, CallbackName => 'EditComponentName', Name => \$EditComponent, CustomField => $CustomField, Object => $Object );
index aaf5517..911e607 100644 (file)
 <textarea cols="<% $Cols %>" rows="<% $Rows %>" name="<% $name %>-Values" id="<% $name %>-Values" class="CF-<%$CustomField->id%>-Edit"><% $Default || '' %></textarea>
 
 <script type="text/javascript">
 <textarea cols="<% $Cols %>" rows="<% $Rows %>" name="<% $name %>-Values" id="<% $name %>-Values" class="CF-<%$CustomField->id%>-Edit"><% $Default || '' %></textarea>
 
 <script type="text/javascript">
-var id = '<% $name . '-Values' %>';
+var id = <% "$name-Values" |n,j%>;
 id = id.replace(/:/g,'\\:');
 jQuery('#'+id).autocomplete( {
 id = id.replace(/:/g,'\\:');
 jQuery('#'+id).autocomplete( {
-    source: "<%RT->Config->Get('WebPath')%>/Helpers/Autocomplete/CustomFieldValues?<% $name . '-Values' %>",
+    source: <%RT->Config->Get('WebPath') |n,j%>+"/Helpers/Autocomplete/CustomFieldValues?"+<% $Context |n,j %>+<% "$name-Values" |n,u,j%>,
     focus: function () {
         // prevent value inserted on focus
         return false;
     focus: function () {
         // prevent value inserted on focus
         return false;
@@ -73,10 +73,10 @@ jQuery('#'+id).autocomplete( {
 % } else {
 <input type="text" id="<% $name %>-Value" name="<% $name %>-Value" class="CF-<%$CustomField->id%>-Edit" value="<% $Default || '' %>"/>
 <script type="text/javascript">
 % } else {
 <input type="text" id="<% $name %>-Value" name="<% $name %>-Value" class="CF-<%$CustomField->id%>-Edit" value="<% $Default || '' %>"/>
 <script type="text/javascript">
-var id = '<% $name . '-Value' %>';
+var id = <% "$name-Value" |n,j%>;
 id = id.replace(/:/g,'\\:');
 jQuery('#'+id).autocomplete( {
 id = id.replace(/:/g,'\\:');
 jQuery('#'+id).autocomplete( {
-    source: "<%RT->Config->Get('WebPath')%>/Helpers/Autocomplete/CustomFieldValues?<% $name . '-Value' %>"
+    source: <%RT->Config->Get('WebPath')|n,j%>+"/Helpers/Autocomplete/CustomFieldValues?"+<% $Context |n,j %>+<% "$name-Value" |n,u,j%>
 }
 );
 % }
 }
 );
 % }
@@ -92,6 +92,11 @@ if ( $Multiple and $Values ) {
         $Default .= $value->Content ."\n";
     }
 }
         $Default .= $value->Content ."\n";
     }
 }
+my $Context = "";
+if ($CustomField->ContextObject) {
+    $Context .= "ContextId="  . $CustomField->ContextObject->Id  . "&";
+    $Context .= "ContextType=". ref($CustomField->ContextObject) . "&";
+}
 </%INIT>
 <%ARGS>
 $CustomField => undef
 </%INIT>
 <%ARGS>
 $CustomField => undef
index b3fefbd..ed6bb14 100644 (file)
@@ -55,7 +55,7 @@
 % if (!$HideCategory and @category and not $CustomField->BasedOnObj->id) {
   <script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/js/cascaded.js"></script>
 %# XXX - Hide this select from w3m?
 % if (!$HideCategory and @category and not $CustomField->BasedOnObj->id) {
   <script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/js/cascaded.js"></script>
 %# XXX - Hide this select from w3m?
-  <select onchange="filter_cascade('<% $id %>-Values', this.value)" name="<% $id %>-Category" class="CF-<%$CustomField->id%>-Edit">
+  <select onchange="filter_cascade(<% "$id-Values" |n,j%>, this.value)" name="<% $id %>-Category" class="CF-<%$CustomField->id%>-Edit">
     <option value=""<% !$selected && qq[ selected="selected"] |n %>><&|/l&>-</&></option>
 %   foreach my $cat (@category) {
 %     my ($depth, $name) = @$cat;
     <option value=""<% !$selected && qq[ selected="selected"] |n %>><&|/l&>-</&></option>
 %   foreach my $cat (@category) {
 %     my ($depth, $name) = @$cat;
 <script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/js/cascaded.js"></script>
 <script type="text/javascript"><!--
 jQuery(  function () {
 <script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/js/cascaded.js"></script>
 <script type="text/javascript"><!--
 jQuery(  function () {
-    var basedon = document.getElementById('<% $NamePrefix . $CustomField->BasedOnObj->id %>-Values');
+    var basedon = document.getElementById(<% $NamePrefix . $CustomField->BasedOnObj->id . "-Values" |n,j%>);
     if (basedon != null) {
         var oldchange = basedon.onchange;
         basedon.onchange = function () {
             filter_cascade(
     if (basedon != null) {
         var oldchange = basedon.onchange;
         basedon.onchange = function () {
             filter_cascade(
-                '<% $id %>-Values',
+                <% "$id-Values" |n,j%>,
                 basedon.value,
                 1
             );
                 basedon.value,
                 1
             );
index 50f3b77..87dfd02 100755 (executable)
@@ -81,7 +81,7 @@ Encode::_utf8_off($error);
 
 $RT::Logger->error($error);
 
 
 $RT::Logger->error($error);
 
-if ( defined $session{'SessionType'} && $session{'SessionType'} eq 'REST' ) {
+if ( $session{'REST'} ) {
     $r->content_type('text/plain');
     $m->out( "Error: " . $Why . "\n" );
     $m->out( $Details . "\n" ) if defined $Details && length $Details;
     $r->content_type('text/plain');
     $m->out( "Error: " . $Why . "\n" );
     $m->out( $Details . "\n" ) if defined $Details && length $Details;
index 5d89fe6..1830c4b 100755 (executable)
@@ -87,7 +87,8 @@ my $head = '';
 #XXX $head .= <& /Elements/Framekiller &>;
 
 if ($Refresh && $Refresh =~ /^(\d+)/ && $1 > 0) {
 #XXX $head .= <& /Elements/Framekiller &>;
 
 if ($Refresh && $Refresh =~ /^(\d+)/ && $1 > 0) {
-  $head .= qq( <meta http-equiv="refresh" content="$Refresh" /> );
+  my $URL = $m->notes->{RefreshURL}; $URL = $URL ? ";URL=$URL" : "";
+  $head .= qq( <meta http-equiv="refresh" content="$1$URL" /> );
 }
 
 my $WebPath = RT->Config->Get('WebPath');
 }
 
 my $WebPath = RT->Config->Get('WebPath');
index e392ac2..28788db 100644 (file)
@@ -60,14 +60,14 @@ $onload => undef
 <script type="text/javascript"><!--
        jQuery( loadTitleBoxStates );
 % if ( $focus ) {
 <script type="text/javascript"><!--
        jQuery( loadTitleBoxStates );
 % if ( $focus ) {
-    jQuery(function () { focusElementById('<% $focus %>') });
+    jQuery(function () { focusElementById(<% $focus |n,j%>) });
 % }
 % if ( $onload ) {
     jQuery( <% $onload |n %> );
 % }
 
 % if ( $RichText and RT->Config->Get('MessageBoxRichText',  $session{'CurrentUser'})) {
 % }
 % if ( $onload ) {
     jQuery( <% $onload |n %> );
 % }
 
 % if ( $RichText and RT->Config->Get('MessageBoxRichText',  $session{'CurrentUser'})) {
-    jQuery().ready(function ()  { ReplaceAllTextareas('<%$m->request_args->{'CKeditorEncoded'} || 0 %>') });
+    jQuery().ready(function ()  { ReplaceAllTextareas(<%$m->request_args->{'CKeditorEncoded'} || 0 |n,j%>) });
 % }
 --></script>
 <%ARGS>
 % }
 --></script>
 <%ARGS>
index 2943cab..61995e0 100755 (executable)
@@ -45,7 +45,7 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<textarea autocomplete="off" class="messagebox" <% $cols |n %> rows="<% $Height %>" <% $wrap_type |n %> name="<% $Name %>" id="<% $Name %>">\
+<textarea autocomplete="off" class="messagebox" <% $width_attr %>="<% $Width %>" rows="<% $Height %>" <% $wrap_type |n %> name="<% $Name %>" id="<% $Name %>">\
 % $m->comp('/Articles/Elements/IncludeArticle', %ARGS);
 % $m->callback( %ARGS, SignatureRef => \$signature );
 <% $Default || '' %><% $message %><% $signature %></textarea>
 % $m->comp('/Articles/Elements/IncludeArticle', %ARGS);
 % $m->callback( %ARGS, SignatureRef => \$signature );
 <% $Default || '' %><% $message %><% $signature %></textarea>
@@ -68,13 +68,16 @@ if ( $IncludeSignature and my $text = $session{'CurrentUser'}->UserObj->Signatur
 # wrap="something" seems to really break IE + richtext
 my $wrap_type = '';
 if ( not RT->Config->Get('MessageBoxRichText',  $session{'CurrentUser'}) ) {
 # wrap="something" seems to really break IE + richtext
 my $wrap_type = '';
 if ( not RT->Config->Get('MessageBoxRichText',  $session{'CurrentUser'}) ) {
-    $wrap_type = qq(wrap="$Wrap");
+    $wrap_type = 'wrap="' . $m->interp->apply_escapes($Wrap, 'h') . '"';
 }
 
 }
 
-# If there's no cols specified, we want to set the width to 100%
-my $cols = 'style="width: 100%"';
-if ( defined $Width and length $Width ) {
-    $cols = qq(cols="$Width");
+# If there's no cols specified, we want to set the width to 100% in CSS
+my $width_attr;
+if ($Width) {
+    $width_attr = 'cols';
+} else {
+    $width_attr = 'style';
+    $Width = 'width: 100%';
 }
 
 </%INIT>
 }
 
 </%INIT>
index 06e2674..ecb219d 100644 (file)
@@ -120,8 +120,10 @@ my $COLUMN_MAP = {
             my $name = 'RemoveCustomField';
             my $checked = $m->request_args->{ $name .'All' }? 'checked="checked"': '';
 
             my $name = 'RemoveCustomField';
             my $checked = $m->request_args->{ $name .'All' }? 'checked="checked"': '';
 
-            return \qq{<input type="checkbox" name="${name}All" value="1" $checked
-                              onclick="setCheckbox(this.form, '$name', this.checked)" />};
+            return \qq{<input type="checkbox" name="}, $name, \qq{All" value="1" $checked
+                              onclick="setCheckbox(this.form, },
+                              $m->interp->apply_escapes($name,'j'),
+                              \qq{, this.checked)" />};
         },
         value => sub {
             my $id = $_[0]->id;
         },
         value => sub {
             my $id = $_[0]->id;
@@ -137,7 +139,7 @@ my $COLUMN_MAP = {
             elsif ( $arg ) {
                 $checked = 'checked="checked"' if $arg == $id;
             }
             elsif ( $arg ) {
                 $checked = 'checked="checked"' if $arg == $id;
             }
-            return \qq{<input type="checkbox" name="$name" value="$id" $checked />}
+            return \qq{<input type="checkbox" name="}, $name, \qq{" value="$id" $checked />}
         },
     },
     MoveCF => {
         },
     },
     MoveCF => {
index 8bc4383..6c366ec 100644 (file)
@@ -111,7 +111,7 @@ my $COLUMN_MAP = {
                 }
             }
 
                 }
             }
 
-            return \('<a href="'.$url.'">'.$frequency.'</a>');
+            return \'<a href="', $url, \'">', $frequency, \'</a>';
         },
     },
     ShowURL => {
         },
     },
     ShowURL => {
index 00655c5..e08dd7c 100644 (file)
@@ -84,12 +84,16 @@ my $COLUMN_MAP = {
         title => 'Encrypt', # loc
         value => sub { return $_[0]->Encrypt? $_[0]->loc('yes') : $_[0]->loc('no') },
     },
         title => 'Encrypt', # loc
         value => sub { return $_[0]->Encrypt? $_[0]->loc('yes') : $_[0]->loc('no') },
     },
+    Lifecycle => {
+        title => 'Lifecycle',
+        attribute => 'Lifecycle',
+        value => sub { return $_[0]->Lifecycle->Name },
+    },
 };
 
 foreach my $field (qw(
     Name Description CorrespondAddress CommentAddress
     InitialPriority FinalPriority DefaultDueIn
 };
 
 foreach my $field (qw(
     Name Description CorrespondAddress CommentAddress
     InitialPriority FinalPriority DefaultDueIn
-    Lifecycle
 )) {
     $COLUMN_MAP->{$field} = {
         title => $field,
 )) {
     $COLUMN_MAP->{$field} = {
         title => $field,
index cc64e24..37a5971 100755 (executable)
@@ -59,8 +59,12 @@ if ($TicketObj) {
     @objects = ($TicketObj);
 } elsif ($QueueObj) {
     @objects = ($QueueObj);
     @objects = ($TicketObj);
 } elsif ($QueueObj) {
     @objects = ($QueueObj);
-} elsif ($cfqueues) {
-    @objects = keys %{$cfqueues};
+} elsif (%Queues) {
+    for my $name (keys %Queues) {
+        my $q = RT::Queue->new($session{'CurrentUser'});
+        $q->Load($name);
+        push @objects, $q;
+    }
 } else {
     # Let's check rights on an empty queue object. that will do a search
     # for any queue.
 } else {
     # Let's check rights on an empty queue object. that will do a search
     # for any queue.
@@ -77,5 +81,5 @@ $m->callback(
 <%ARGS>
 $TicketObj  => undef
 $QueueObj   => undef
 <%ARGS>
 $TicketObj  => undef
 $QueueObj   => undef
-$cfqueues   => undef
+%Queues     => ()
 </%ARGS>
 </%ARGS>
index cf2010a..81b3838 100644 (file)
@@ -78,7 +78,7 @@ my $query = $m->comp('/Elements/QueryString',
 <script type="text/javascript">
     jQuery(function() {
         var cache = {};
 <script type="text/javascript">
     jQuery(function() {
         var cache = {};
-        jQuery("#<% $Name %>").autocomplete({
+        jQuery("#"+<% $Name |n,j%>).autocomplete({
             minLength: 2,
             source: function(request, response) {
                 if ( request.term in cache ) {
             minLength: 2,
             source: function(request, response) {
                 if ( request.term in cache ) {
@@ -86,7 +86,7 @@ my $query = $m->comp('/Elements/QueryString',
                 }
                 else {
                     jQuery.ajax({
                 }
                 else {
                     jQuery.ajax({
-                        url: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Owners?<% $query|n %>",
+                        url: <% RT->Config->Get('WebPath')|n,j%>+"/Helpers/Autocomplete/Owners?"+<% $query|n,j %>,
                         dataType: "json",
                         data: request,
                         success: function( data ) {
                         dataType: "json",
                         data: request,
                         success: function( data ) {
index e571baf..af1ff61 100755 (executable)
@@ -66,6 +66,8 @@ if ( @Statuses ) {
 }
 elsif ( $TicketObj ) {
     my $current = $TicketObj->Status;
 }
 elsif ( $TicketObj ) {
     my $current = $TicketObj->Status;
+    push @status, $current;
+
     my $lifecycle = $TicketObj->QueueObj->Lifecycle;
 
     my %has = ();
     my $lifecycle = $TicketObj->QueueObj->Lifecycle;
 
     my %has = ();
@@ -78,8 +80,15 @@ elsif ( $TicketObj ) {
 }
 elsif ( $QueueObj ) {
     @status = $QueueObj->Lifecycle->Transitions('');
 }
 elsif ( $QueueObj ) {
     @status = $QueueObj->Lifecycle->Transitions('');
-}
-else {
+} elsif ( %Queues ) {
+    for my $id (keys %Queues) {
+        my $queue = RT::Queue->new($session{'CurrentUser'});
+        $queue->Load($id);
+        push @status, $queue->Lifecycle->Valid if $queue->id;
+    }
+    my %seen;
+    @status = grep { not $seen{$_}++ } @status;
+} else {
     @status = RT::Queue->Lifecycle->Valid;
 }
 </%INIT>
     @status = RT::Queue->Lifecycle->Valid;
 }
 </%INIT>
@@ -89,6 +98,7 @@ $Name => undef
 @Statuses => ()
 $TicketObj => undef
 $QueueObj => undef
 @Statuses => ()
 $TicketObj => undef
 $QueueObj => undef
+%Queues => ()
 
 $Default => ''
 $SkipDeleted => 0
 
 $Default => ''
 $SkipDeleted => 0
index fcd530e..6059f4e 100644 (file)
@@ -114,12 +114,12 @@ my $print_value = sub {
        my $vid = $value->id;
        $m->out(   '<div class="object_cf_value_include" id="object_cf_value_'. $vid .'">' );
        $m->out( loc("See also:") );
        my $vid = $value->id;
        $m->out(   '<div class="object_cf_value_include" id="object_cf_value_'. $vid .'">' );
        $m->out( loc("See also:") );
-       $m->out(   '<a href="'. $value->IncludeContentForValue .'">' );
-       $m->out( $value->IncludeContentForValue );
+       $m->out(   '<a href="'. $m->interp->apply_escapes($value->IncludeContentForValue, 'h') .'">' );
+       $m->out( $m->interp->apply_escapes($value->IncludeContentForValue, 'h') );
        $m->out(   qq{</a></div>\n} );
        $m->out(   qq{</a></div>\n} );
-       $m->out(   qq{<script><!--\njQuery('#object_cf_value_$vid').load('} );
-       $m->out( $value->IncludeContentForValue );
-       $m->out(   qq{');\n--></script>\n} );
+       $m->out(   qq{<script><!--\njQuery('#object_cf_value_$vid').load(} );
+       $m->out(   $m->interp->apply_escapes($value->IncludeContentForValue, 'j') );
+       $m->out(   qq{);\n--></script>\n} );
     }
 };
 
     }
 };
 
index 8913a32..1727fa3 100644 (file)
@@ -45,7 +45,7 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<a href="<%$URI->Resolver->HREF%>">
+<a href="<% $href %>">
 % if ($URI->IsLocal) {
 % my $member = $URI->Object;
 % my $has_name = UNIVERSAL::can($member, 'Name') || (UNIVERSAL::can($member, '_Accessible') && $member->_Accessible('Name', 'read'));
 % if ($URI->IsLocal) {
 % my $member = $URI->Object;
 % my $has_name = UNIVERSAL::can($member, 'Name') || (UNIVERSAL::can($member, '_Accessible') && $member->_Accessible('Name', 'read'));
 <%ARGS>
 $URI => undef
 </%ARGS>
 <%ARGS>
 $URI => undef
 </%ARGS>
+
+<%INIT>
+my $href = $URI->Resolver->HREF;
+if ( $URI->IsLocal ) {
+    my $base = RT->Config->Get('WebBaseURL');
+    # URI->rel doesn't contain the leading '/'
+    $href = '/' . URI->new($href)->rel($base);
+}
+</%INIT>
index 2b23181..4b96bbf 100644 (file)
@@ -64,12 +64,12 @@ my $query_link_url = RT->Config->Get('WebPath').'/Search/Results.html';
 if ($SavedSearch) {
     my ( $container_object, $search_id ) = _parse_saved_search($SavedSearch);
     unless ( $container_object ) {
 if ($SavedSearch) {
     my ( $container_object, $search_id ) = _parse_saved_search($SavedSearch);
     unless ( $container_object ) {
-        $m->out(loc("Either you have no rights to view saved search [_1] or identifier is incorrect", $SavedSearch));
+        $m->out(loc("Either you have no rights to view saved search [_1] or identifier is incorrect", $m->interp->apply_escapes($SavedSearch, 'h')));
         return;
     }
     $search = $container_object->Attributes->WithId($search_id);
     unless ( $search->Id && ref( $SearchArg = $search->Content ) eq 'HASH' ) {
         return;
     }
     $search = $container_object->Attributes->WithId($search_id);
     unless ( $search->Id && ref( $SearchArg = $search->Content ) eq 'HASH' ) {
-        $m->out(loc("Saved Search [_1] not found", $SavedSearch)) unless $IgnoreMissing;
+        $m->out(loc("Saved Search [_1] not found", $m->interp->apply_escapes($SavedSearch, 'h'))) unless $IgnoreMissing;
         return;
     }
     $SearchArg->{'SavedSearchId'} ||= $SavedSearch;
         return;
     }
     $SearchArg->{'SavedSearchId'} ||= $SavedSearch;
@@ -79,7 +79,7 @@ if ($SavedSearch) {
         # XXX: dispatch to different handler here
         $query_display_component
             = '/Search/Elements/' . $SearchArg->{SearchType};
         # XXX: dispatch to different handler here
         $query_display_component
             = '/Search/Elements/' . $SearchArg->{SearchType};
-        $query_link_url = RT->Config->Get('WebURL') . "/Search/$SearchArg->{SearchType}.html";
+        $query_link_url = RT->Config->Get('WebPath') . "/Search/$SearchArg->{SearchType}.html";
     } elsif ($ShowCustomize) {
         $customize = RT->Config->Get('WebPath') . '/Search/Build.html?'
             . $m->comp( '/Elements/QueryString',
     } elsif ($ShowCustomize) {
         $customize = RT->Config->Get('WebPath') . '/Search/Build.html?'
             . $m->comp( '/Elements/QueryString',
@@ -93,7 +93,7 @@ if ($SavedSearch) {
             if ($custom->Description eq $Name) { $search = $custom; last }
         }
         unless ($search && $search->id) {
             if ($custom->Description eq $Name) { $search = $custom; last }
         }
         unless ($search && $search->id) {
-            $m->out("Predefined search $Name not found");
+            $m->out(loc("Predefined search [_1] not found", $m->interp->apply_escapes($Name, 'h')));
             return;
         }
     }
             return;
         }
     }
index 044ec4c..3654977 100644 (file)
@@ -51,7 +51,7 @@
 # $Address is Email::Address object
 
 my $comp = '/Elements/ShowUser'. ucfirst lc $style;
 # $Address is Email::Address object
 
 my $comp = '/Elements/ShowUser'. ucfirst lc $style;
-unless ( $m->comp_exists( $comp ) ) {
+unless ( RT::Interface::Web->ComponentPathIsSafe($comp) and $m->comp_exists( $comp ) ) {
     $RT::Logger->error(
         'Either system config or user #'
         . $session{'CurrentUser'}->id
     $RT::Logger->error(
         'Either system config or user #'
         . $session{'CurrentUser'}->id
index cbf3f58..b7840d3 100755 (executable)
@@ -52,10 +52,10 @@ id="<%$id%>"
 >
   <div class="extra-buttons">
 % if ($CheckAll) {
 >
   <div class="extra-buttons">
 % if ($CheckAll) {
-  <input type="button" value="<%$CheckAllLabel%>" onclick="setCheckbox(this.form, <% length $CheckboxName ? qq{'$CheckboxName'} : length $CheckboxNameRegex ? $CheckboxNameRegex : q{''} %>, true);return false;" class="button" />
+  <input type="button" value="<%$CheckAllLabel%>" onclick="setCheckbox(this.form, <% $match %>, true);return false;" class="button" />
 % }
 % if ($ClearAll) {
 % }
 % if ($ClearAll) {
-  <input type="button" value="<%$ClearAllLabel%>" onclick="setCheckbox(this.form, <% length $CheckboxName ? qq{'$CheckboxName'} : length $CheckboxNameRegex ? $CheckboxNameRegex : q{''} %>, false);return false;" class="button" />
+  <input type="button" value="<%$ClearAllLabel%>" onclick="setCheckbox(this.form, <% $match %>, false);return false;" class="button" />
 % }
 % if ($Reset) {
   <input type="reset" value="<%$ResetLabel%>" class="button" />
 % }
 % if ($Reset) {
   <input type="reset" value="<%$ResetLabel%>" class="button" />
@@ -115,3 +115,13 @@ $ResetLabel => loc('Reset')
 $SubmitId => undef
 $id => undef
 </%ARGS>
 $SubmitId => undef
 $id => undef
 </%ARGS>
+<%init>
+my $match;
+if (length $CheckboxName) {
+    $match = $m->interp->apply_escapes($CheckboxName,'j');
+} elsif (length $CheckboxNameRegex) {
+    $match = $CheckboxNameRegex;
+} else {
+    $match = q{''};
+}
+</%init>
index 75b8160..3193b48 100755 (executable)
@@ -734,6 +734,9 @@ my $build_main_nav = sub {
             $current_search_menu->child( bulk  => title => loc('Bulk Update'), path => "/Search/Bulk.html$args" );
             $current_search_menu->child( chart => title => loc('Chart'),       path => "/Search/Chart.html$args" );
 
             $current_search_menu->child( bulk  => title => loc('Bulk Update'), path => "/Search/Bulk.html$args" );
             $current_search_menu->child( chart => title => loc('Chart'),       path => "/Search/Chart.html$args" );
 
+            #formerly Callbacks/RTx-Calendar/Ticket/Element/Tabs/Default
+            $current_search_menu->child( calendar => title => loc('Calendar'), path => "/Search/Calendar.html$args" );
+
             my $more = $current_search_menu->child( more => title => loc('Feeds') );
 
             $more->child( tsv => title => loc('TSV'), path => "/Search/Results.tsv$args" );
             my $more = $current_search_menu->child( more => title => loc('Feeds') );
 
             $more->child( tsv => title => loc('TSV'), path => "/Search/Results.tsv$args" );
index b8b21e4..887302f 100644 (file)
 # Only autocomplete the last value
 my $term = (split /\n/, $ARGS{term} || '')[-1];
 
 # Only autocomplete the last value
 my $term = (split /\n/, $ARGS{term} || '')[-1];
 
+my $abort = sub {
+    $r->content_type('application/json');
+    $m->out(JSON::to_json( [] ));
+    $m->abort;
+};
+
+unless ( exists $ARGS{ContextType} and exists $ARGS{ContextId} ) {
+    RT->Logger->debug("No context provided");
+    $abort->();
+}
+
 my $CustomField;
 for my $k ( keys %ARGS ) {
     next unless $k =~ /^Object-.*?-\d*-CustomField-(\d+)-Values?$/;
 my $CustomField;
 for my $k ( keys %ARGS ) {
     next unless $k =~ /^Object-.*?-\d*-CustomField-(\d+)-Values?$/;
@@ -59,9 +70,38 @@ for my $k ( keys %ARGS ) {
     last;
 }
 
     last;
 }
 
-$m->abort unless $CustomField;
+unless ( $CustomField ) {
+    RT->Logger->debug("No CustomField provided");
+    $abort->();
+}
+
+my $SystemCustomFieldObj = RT::CustomField->new( RT->SystemUser );
+my ($id, $msg) = $SystemCustomFieldObj->LoadById( $CustomField ) ;
+unless ( $id ) {
+    RT->Logger->debug("Invalid CustomField provided: $msg");
+    $abort->();
+}
+
+my $context_object = $SystemCustomFieldObj->LoadContextObject(
+    $ARGS{ContextType}, $ARGS{ContextId} );
+$abort->() unless $context_object;
+
 my $CustomFieldObj = RT::CustomField->new( $session{'CurrentUser'} );
 my $CustomFieldObj = RT::CustomField->new( $session{'CurrentUser'} );
-$CustomFieldObj->Load( $CustomField );
+if ( $SystemCustomFieldObj->ValidateContextObject($context_object) ) {
+    # drop our privileges that came from calling LoadContextObject as the System User
+    $context_object->new($session{'CurrentUser'});
+    $context_object->LoadById($ARGS{ContextId});
+    $CustomFieldObj->SetContextObject( $context_object );
+} else {
+    RT->Logger->debug("Invalid Context Object ".$context_object->id." for Custom Field ".$SystemCustomFieldObj->id);
+    $abort->();
+}
+
+($id, $msg) = $CustomFieldObj->LoadById( $CustomField );
+unless ( $CustomFieldObj->Name ) {
+    RT->Logger->debug("Current User cannot see this Custom Field, terminating");
+    $abort->();
+}
 
 my $values = $CustomFieldObj->Values;
 $values->Limit(
 
 my $values = $CustomFieldObj->Values;
 $values->Limit(
index bb90b98..68e8a05 100644 (file)
@@ -47,7 +47,9 @@
 %# END BPS TAGGED BLOCK }}}
 <%INIT>
 my $TicketTemplate = "/Ticket/Elements/ShowRequestorTickets$Status";
 %# END BPS TAGGED BLOCK }}}
 <%INIT>
 my $TicketTemplate = "/Ticket/Elements/ShowRequestorTickets$Status";
-$TicketTemplate = "/Ticket/Elements/ShowRequestorTicketsActive" unless $m->comp_exists($TicketTemplate);
+$TicketTemplate = "/Ticket/Elements/ShowRequestorTicketsActive"
+    unless RT::Interface::Web->ComponentPathIsSafe($TicketTemplate)
+       and $m->comp_exists($TicketTemplate);
 my $user_obj = RT::User->new($session{CurrentUser});
 my ($val, $msg) = $user_obj->Load($Requestor);
 unless ($val) {
 my $user_obj = RT::User->new($session{CurrentUser});
 my ($val, $msg) = $user_obj->Load($Requestor);
 unless ($val) {
index 3312b57..68f8a67 100644 (file)
@@ -58,7 +58,7 @@
 <&|/l&>SQLite is a database that doesn't need a server or any configuration whatsoever. RT's authors recommend it for testing, demoing and development, but it's not quite right for a high-volume production RT server.</&>
 </b></p>
 <p>
 <&|/l&>SQLite is a database that doesn't need a server or any configuration whatsoever. RT's authors recommend it for testing, demoing and development, but it's not quite right for a high-volume production RT server.</&>
 </b></p>
 <p>
-<&|/l, '<a href="http://search.cpan.org" target="_new">CPAN</a>' &>If your preferred database isn't listed in the dropdown below, that means RT couldn't find a <i>database driver</i> for it installed locally. You may be able to remedy this by using [_1] to download and install DBD::MySQL, DBD::Oracle or DBD::Pg.</&>
+<&|/l_unsafe, '<a href="http://search.cpan.org" target="_new">CPAN</a>' &>If your preferred database isn't listed in the dropdown below, that means RT couldn't find a <i>database driver</i> for it installed locally. You may be able to remedy this by using [_1] to download and install DBD::MySQL, DBD::Oracle or DBD::Pg.</&>
 </p>
 </div>
 
 </p>
 </div>
 
index ee81e70..24ac0ff 100644 (file)
@@ -53,7 +53,7 @@
 </p>
 
 <p>
 </p>
 
 <p>
-<&|/l, '<tt>root</tt>' &>You should be taken directly to a login page. You'll be able to log in with username of [_1] and the password you set earlier.</&>
+<&|/l_unsafe, '<tt>root</tt>' &>You should be taken directly to a login page. You'll be able to log in with username of [_1] and the password you set earlier.</&>
 </p>
 
 <p>
 </p>
 
 <p>
index 47d7616..0cc39af 100644 (file)
@@ -125,7 +125,7 @@ if ( $Run ) {
                 $RT::Handle = RT::Handle->new;
                 RT::Init();
                 my $file = $RT::EtcPath . "/initialdata";
                 $RT::Handle = RT::Handle->new;
                 RT::Init();
                 my $file = $RT::EtcPath . "/initialdata";
-                ($status, $msg) = $RT::Handle->InsertData( $file );
+                ($status, $msg) = $RT::Handle->InsertData( $file, undef, disconnect_after => 0 );
             }
             unless ( $status ) {
                 push @errors, loc('ERROR: [_1]', $msg);
             }
             unless ( $status ) {
                 push @errors, loc('ERROR: [_1]', $msg);
index 61fb89e..78069af 100644 (file)
@@ -92,7 +92,7 @@
 my @errors;
 my $locked;
 
 my @errors;
 my $locked;
 
-my $file = File::Spec->catfile( $RT::EtcPath, 'RT_SiteConfig.pm' );
+my $file = RT::Installer->ConfigFile;
 
 if ( ! -e $file ) {
     # write a blank RT_SiteConfig.pm
 
 if ( ! -e $file ) {
     # write a blank RT_SiteConfig.pm
index b8e119a..20024cc 100755 (executable)
@@ -81,5 +81,5 @@ if (keys %session) {
 }
 
 $m->callback( %ARGS, CallbackName => 'AfterSessionDelete' );
 }
 
 $m->callback( %ARGS, CallbackName => 'AfterSessionDelete' );
-$m->notes->{LogoutURL} = $URL;
+$m->notes->{RefreshURL} = $URL;
 </%INIT>
 </%INIT>
index aff24d8..e6d4cb3 100644 (file)
@@ -45,9 +45,6 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<!--[if (lt IE 9)&(gt IE 6)]>
-<link rel="stylesheet" href="<%RT->Config->Get('WebPath')%>/NoAuth/css/aileron/msie-pie.css" type="text/css" media="all" />
-<![endif]-->
 <!--[if lt IE 8]>
 <link rel="stylesheet" href="<%RT->Config->Get('WebPath')%>/NoAuth/css/aileron/msie.css" type="text/css" media="all" />
 <![endif]-->
 <!--[if lt IE 8]>
 <link rel="stylesheet" href="<%RT->Config->Get('WebPath')%>/NoAuth/css/aileron/msie.css" type="text/css" media="all" />
 <![endif]-->
index f90ac9f..ed6623c 100644 (file)
 
 .titlebox .titlebox-title {
  position: relative;
 
 .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 {
 }
 
 .titlebox .titlebox-title a {
diff --git a/rt/share/html/NoAuth/css/aileron/msie-pie.css b/rt/share/html/NoAuth/css/aileron/msie-pie.css
deleted file mode 100644 (file)
index baa9ebe..0000000
+++ /dev/null
@@ -1,58 +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 }}}
-.search-result-views,
-.ticket-transaction div.metadata span.actions,
-div#ticket-history div.downloadattachment, 
-.ticket-transaction div.metadata  span.type,
-.titlebox,
-.titlebox .titlebox-title .right,
-.titlebox .titlebox-title .left,
-div#footer,
-div#body {
-    behavior: url(<%RT->Config->Get('WebPath')%>/NoAuth/css/images/PIE.htc);
-}
diff --git a/rt/share/html/NoAuth/css/images/PIE.htc b/rt/share/html/NoAuth/css/images/PIE.htc
deleted file mode 100644 (file)
index 6a40cef..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<!--
-PIE: CSS3 rendering for IE
-Version 1.0beta2
-http://css3pie.com
-Dual-licensed for use under the Apache License Version 2.0 or the General Public License (GPL) Version 2.
--->
-<PUBLIC:COMPONENT lightWeight="true">
-    <PUBLIC:ATTACH EVENT="onresize" FOR="element" ONEVENT="update()" />
-    <PUBLIC:ATTACH EVENT="onresize" FOR="window" ONEVENT="update()" />
-    <PUBLIC:ATTACH EVENT="onmove" FOR="element" ONEVENT="update()" />
-    <PUBLIC:ATTACH EVENT="onpropertychange" FOR="element" ONEVENT="propChanged()" />
-    <PUBLIC:ATTACH EVENT="onmouseenter" FOR="element" ONEVENT="mouseEntered()" />
-    <PUBLIC:ATTACH EVENT="onmouseleave" FOR="element" ONEVENT="mouseLeft()" />
-    <PUBLIC:ATTACH EVENT="oncontentready" FOR="element" ONEVENT="update()" />
-    <PUBLIC:ATTACH EVENT="ondocumentready" FOR="element" ONEVENT="update()" />
-    <PUBLIC:ATTACH EVENT="ondetach" FOR="element" ONEVENT="cleanup()" />
-
-    <script type="text/javascript">
-function i(){return function(){}}var D=window.PIE;
-if(!D){D=window.PIE={T:"-pie-",Ma:"Pie",Ka:"pie_"};if(!window.XMLHttpRequest){D.Yb=true;D.T=D.T.replace(/^-/,"")}D.oa=element.document.documentMode;D.Da=!!D.oa;if(D.oa===8){D.Ca={ya:{},add:function(a){this.ya[a.id||(a.id=""+(new Date).getTime()+Math.random())]=a},remove:function(a){delete this.ya[a.id]},Tb:function(){var a=this.ya,b;for(b in a)a.hasOwnProperty(b)&&a[b]()}};setInterval(function(){D.Ca.Tb()},250)}D.q={ja:function(a){var b=D.Gb;if(!b){b=D.Gb=element.document.createDocumentFragment();
-b.namespaces.add("css3vml","urn:schemas-microsoft-com:vml")}return b.createElement("css3vml:"+a)},Fa:function(a){var b,c,d,e,f=arguments;b=1;for(c=f.length;b<c;b++){e=f[b];for(d in e)if(e.hasOwnProperty(d))a[d]=e[d]}return a},ib:function(a,b,c){var d=D.Ab||(D.Ab={}),e=d[a],f;if(e)b.call(c,e);else{f=new Image;f.onload=function(){e=d[a]={v:f.width,i:f.height};b.call(c,e);f.onload=null};f.src=a}}};D.g=function(){function a(b){this.F=b}a.prototype={Ga:/(px|em|ex|mm|cm|in|pt|pc|%)$/,Va:function(){var b=
-this.Bb;if(b===undefined)b=this.Bb=parseFloat(this.F);return b},Aa:function(){var b=this.ua;if(!b)b=this.ua=(b=this.F.match(this.Ga))&&b[0]||"px";return b},a:function(b,c){var d=this.Va(),e=this.Aa();switch(e){case "px":return d;case "%":return d*(typeof c==="function"?c():c)/100;case "em":return d*this.Ua(b);case "ex":return d*this.Ua(b)/2;default:return d*a.Ob[e]}},Ua:function(b){var c=b.currentStyle.fontSize,d;if(c.indexOf("px")>0)return parseFloat(c);else{c=this.Fb;if(!c){c=this.Fb=b.document.createElement("length-calc");
-d=c.style;d.width="1em";d.position="absolute";d.top=d.left=-9999}b.appendChild(c);d=c.offsetWidth;b.removeChild(c);return d}}};a.Ob=function(){for(var b=["mm","cm","in","pt","pc"],c={},d=element.parentNode,e=0,f=b.length,j,g,h;e<f;e++){j=b[e];g=element.document.createElement("length-calc");h=g.style;h.position="absolute";h.top=h.left=-9999;h.width="100"+j;d.appendChild(g);c[j]=g.offsetWidth/100;d.removeChild(g)}return c}();a.Oa=new a("0");return a}();D.ra=function(){function a(b){this.C=b}a.prototype=
-{Vb:function(){if(!this.Qa){var b=this.C,c=b.length,d=D.g.Oa,e=new D.g("50%"),f=D.k.Y.V,j={top:1,center:1,bottom:1},g={left:1,center:1,right:1};d=["left",d,"top",d];if(c===1){b.push({type:f,value:"center"});c++}if(c===2){f&(b[0].type|b[1].type)&&b[0].value in j&&b[1].value in g&&b.push(b.shift());if(b[0].type&f)if(b[0].value==="center")d[1]=e;else d[0]=b[0].value;else if(b[0].J())d[1]=new D.g(b[0].value);if(b[1].type&f)if(b[1].value==="center")d[3]=e;else d[2]=b[1].value;else if(b[1].J())d[3]=new D.g(b[1].value)}this.Qa=
-d}return this.Qa},coords:function(b,c,d){var e=this.Vb(),f=e[1].a(b,c);b=e[3].a(b,d);return{x:Math.round(e[0]==="right"?c-f:f),y:Math.round(e[2]==="bottom"?d-b:b)}}};return a}();D.kb=function(){function a(b){this.F=b}a.prototype={Ga:/[a-z]+$/i,Aa:function(){return this.ua||(this.ua=this.F.match(this.Ga)[0].toLowerCase())},Pb:function(){var b=this.zb,c;if(b===undefined){b=this.Aa();c=parseFloat(this.F,10);b=this.zb=b==="deg"?c:b==="rad"?c/Math.PI*180:b==="grad"?c/400*360:b==="turn"?c*360:0}return b}};
-return a}();D.U=function(){function a(b){this.F=b}a.ec=/\s*rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d+|\d*\.\d+)\s*\)\s*/;a.prototype={parse:function(){if(!this.ha){var b=this.F,c=b.match(a.ec);if(c){this.ha="rgb("+c[1]+","+c[2]+","+c[3]+")";this.Pa=parseFloat(c[4])}else{this.ha=b;this.Pa=1}}},value:function(b){this.parse();return this.ha==="currentColor"?b.currentStyle.color:this.ha},Ra:function(){this.parse();return this.Pa}};return a}();D.k=function(){function a(c){this.ka=c;
-this.ch=0;this.C=[];this.ea=0}var b=a.Y={fa:1,Ja:2,S:4,ub:8,La:16,V:32,n:64,W:128,X:256,ga:512,wb:1024,URL:2048};a.Na=function(c,d){this.type=c;this.value=d};a.Na.prototype={Ea:function(){return this.type&b.n||this.type&b.W&&this.value==="0"},J:function(){return this.Ea()||this.type&b.ga}};a.prototype={mc:/\s/,$b:/^[\+\-]?(\d*\.)?\d+/,url:/^url\(\s*("([^"]*)"|'([^']*)'|([!#$%&*-~]*))\s*\)/i,Ya:/^\-?[_a-z][\w-]*/i,hc:/^("([^"]*)"|'([^']*)')/,Wb:/^#([\da-f]{6}|[\da-f]{3})/i,kc:{px:b.n,em:b.n,ex:b.n,
-mm:b.n,cm:b.n,"in":b.n,pt:b.n,pc:b.n,deg:b.fa,rad:b.fa,grad:b.fa},Lb:{aqua:1,black:1,blue:1,fuchsia:1,gray:1,green:1,lime:1,maroon:1,navy:1,olive:1,purple:1,red:1,silver:1,teal:1,white:1,yellow:1,currentColor:1},Kb:{rgb:1,rgba:1,hsl:1,hsla:1},next:function(c){function d(n,p){n=new a.Na(n,p);if(!c){k.C.push(n);k.ea++}return n}function e(){k.ea++;return null}var f,j,g,h,k=this;if(this.ea<this.C.length)return this.C[this.ea++];for(;this.mc.test(this.ka.charAt(this.ch));)this.ch++;if(this.ch>=this.ka.length)return e();
-j=this.ch;f=this.ka.substring(this.ch);g=f.charAt(0);switch(g){case "#":if(h=f.match(this.Wb)){this.ch+=h[0].length;return d(b.S,h[0])}break;case '"':case "'":if(h=f.match(this.hc)){this.ch+=h[0].length;return d(b.wb,h[2]||h[3]||"")}break;case "/":case ",":this.ch++;return d(b.X,g);case "u":if(h=f.match(this.url)){this.ch+=h[0].length;return d(b.URL,h[2]||h[3]||h[4]||"")}}if(h=f.match(this.$b)){g=h[0];this.ch+=g.length;if(f.charAt(g.length)==="%"){this.ch++;return d(b.ga,g+"%")}if(h=f.substring(g.length).match(this.Ya)){g+=
-h[0];this.ch+=h[0].length;return d(this.kc[h[0].toLowerCase()]||b.ub,g)}return d(b.W,g)}if(h=f.match(this.Ya)){g=h[0];this.ch+=g.length;if(g.toLowerCase()in this.Lb)return d(b.S,g);if(f.charAt(g.length)==="("){this.ch++;if(g.toLowerCase()in this.Kb){f=function(n){return n&&n.type&b.W};h=function(n){return n&&n.type&(b.W|b.ga)};var o=function(n,p){return n&&n.value===p},m=function(){return k.next(1)};if((g.charAt(0)==="r"?h(m()):f(m()))&&o(m(),",")&&h(m())&&o(m(),",")&&h(m())&&(g==="rgb"||g==="hsa"||
-o(m(),",")&&f(m()))&&o(m(),")"))return d(b.S,this.ka.substring(j,this.ch));return e()}return d(b.La,g+"(")}return d(b.V,g)}this.ch++;return d(b.Ja,g)},m:function(){return this.C[this.ea-- -2]},all:function(){for(;this.next(););return this.C},R:function(c,d){for(var e=[],f,j;f=this.next();){if(c(f)){j=true;this.m();break}e.push(f)}return d&&!j?null:e}};return a}();D.w={M:function(a){function b(c){this.element=c}D.q.Fa(b.prototype,D.w,a);return b},f:function(){if(this.j())this.Eb=this.N(this.yb=this.ba());
-return this.Eb},ba:function(){var a=this.element,b=a.style;a=a.currentStyle;var c=this.aa,d=this.da,e=this.Cb||(this.Cb=D.T+c),f=this.Db||(this.Db=D.Ma+d.charAt(0).toUpperCase()+d.substring(1));return b[f]||a.getAttribute(e)||b[d]||a.getAttribute(c)},d:function(){return!!this.f()},j:function(){return this.yb!==this.ba()}};D.mb=D.w.M({aa:D.T+"background",da:D.Ma+"Background",Ib:{scroll:1,fixed:1,local:1},qa:{"repeat-x":1,"repeat-y":1,repeat:1,"no-repeat":1},ac:{"padding-box":1,"border-box":1,"content-box":1},
-Jb:{"padding-box":1,"border-box":1},dc:{top:1,right:1,bottom:1,left:1,center:1},fc:{contain:1,cover:1},N:function(a){function b(q){return q.J()||q.type&h&&q.value in n}function c(q){return q.J()&&new D.g(q.value)||q.value==="auto"&&"auto"}var d=this.element.currentStyle,e,f,j=D.k.Y,g=j.X,h=j.V,k=j.S,o,m,n=this.dc,p,t,r=null;if(this.za()){a=new D.k(a);r={images:[]};for(f={};e=a.next();){o=e.type;m=e.value;if(!f.type&&o&j.La&&m==="linear-gradient("){p={P:[],type:"linear-gradient"};for(t={};e=a.next();){o=
-e.type;m=e.value;if(o&j.Ja&&m===")"){t.color&&p.P.push(t);p.P.length>1&&D.q.Fa(f,p);break}if(o&k){if(p.wa||p.Ba){e=a.m();if(e.type!==g)break;a.next()}t={color:new D.U(m)};e=a.next();if(e.J())t.Za=new D.g(e.value);else a.m()}else if(o&j.fa&&!p.wa&&!t.color&&!p.P.length)p.wa=new D.kb(e.value);else if(b(e)&&!p.Ba&&!t.color&&!p.P.length){a.m();p.Ba=new D.ra(a.R(function(q){return!b(q)},false))}else if(o&g&&m===","){if(t.color){p.P.push(t);t={}}}else break}}else if(!f.type&&o&j.URL){f.url=m;f.type="image"}else if(b(e)&&
-!f.size){a.m();f.position=new D.ra(a.R(function(q){return!b(q)},false))}else if(o&h)if(m in this.qa)f.repeat=m;else if(m in this.ac){f.origin=m;if(m in this.Jb)f.clip=m}else{if(m in this.Ib)f.rc=m}else if(o&k&&!r.color)r.color=new D.U(m);else if(o&g)if(m==="/"){e=a.next();o=e.type;m=e.value;if(o&h&&m in this.fc)f.size=m;else if(m=c(e))f.size={v:m,i:c(a.next())||a.m()&&m}}else{if(m===","&&f.type){r.images.push(f);f={}}}else return null}f.type&&r.images.push(f)}else this.gb(function(){var q=d.backgroundPositionX,
-w=d.backgroundPositionY,l=d.backgroundImage,u=d.backgroundColor;r={};if(u!=="transparent")r.color=new D.U(u);if(l!=="none")r.images=[{type:"image",url:(new D.k(l)).next().value,repeat:d.backgroundRepeat,position:new D.ra((new D.k(q+" "+w)).all())}]});return r},gb:function(a){var b=this.element.runtimeStyle,c=b.backgroundImage,d=b.backgroundColor;b.backgroundImage=b.backgroundColor="";a=a.call(this);b.backgroundImage=c;b.backgroundColor=d;return a},ba:function(){var a=this.element.currentStyle;return this.za()||
-this.gb(function(){return a.backgroundColor+" "+a.backgroundImage+" "+a.backgroundRepeat+" "+a.backgroundPositionX+" "+a.backgroundPositionY})},za:function(){var a=this.element;return a.style[this.da]||a.currentStyle.getAttribute(this.aa)},d:function(){return this.za()&&!!this.f()}});D.qb=D.w.M({bb:["Top","Right","Bottom","Left"],Zb:{uc:"1px",sc:"3px",tc:"5px"},N:function(){var a={},b={},c={},d=false,e=true,f=true,j=true;this.hb(function(){for(var g=this.element.currentStyle,h=0,k,o,m,n,p,t,r;h<4;h++){m=
-this.bb[h];r=m.charAt(0).toLowerCase();k=b[r]=g["border"+m+"Style"];o=g["border"+m+"Color"];m=g["border"+m+"Width"];if(h>0){if(k!==n)f=false;if(o!==p)e=false;if(m!==t)j=false}n=k;p=o;t=m;c[r]=new D.U(o);m=a[r]=new D.g(b[r]==="none"?"0":this.Zb[m]||m);if(m.a(this.element)>0)d=true}});return d?{fb:a,ic:b,Mb:c,nc:j,Nb:e,jc:f}:null},ba:function(){var a=this.element.currentStyle,b;this.hb(function(){b=a.borderWidth+"|"+a.borderStyle+"|"+a.borderColor});return b},hb:function(a){var b=this.element.runtimeStyle,
-c=b.borderWidth,d=b.borderStyle,e=b.borderColor;b.borderWidth=b.borderStyle=b.borderColor="";a=a.call(this);b.borderWidth=c;b.borderStyle=d;b.borderColor=e;return a}});(function(){D.sa=D.w.M({aa:"border-radius",da:"borderRadius",N:function(b){var c=null,d,e,f,j,g=false;if(b){e=new D.k(b);var h=function(){for(var k=[],o;(f=e.next())&&f.J();){j=new D.g(f.value);o=j.Va();if(o<0)return null;if(o>0)g=true;k.push(j)}return k.length>0&&k.length<5?{tl:k[0],tr:k[1]||k[0],br:k[2]||k[0],bl:k[3]||k[1]||k[0]}:
-null};if(b=h()){if(f){if(f.type&D.k.Y.X&&f.value==="/")d=h()}else d=b;if(g&&b&&d)c={x:b,y:d}}}return c}});var a=D.g.Oa;a={tl:a,tr:a,br:a,bl:a};D.sa.jb={x:a,y:a}})();D.ob=D.w.M({aa:"border-image",da:"borderImage",qa:{stretch:1,round:1,repeat:1,space:1},N:function(a){var b=null,c,d,e,f,j,g,h=0,k,o=D.k.Y,m=o.V,n=o.W,p=o.n,t=o.ga;if(a){c=new D.k(a);b={};for(var r=function(l){return l&&l.type&o.X&&l.value==="/"},q=function(l){return l&&l.type&m&&l.value==="fill"},w=function(){f=c.R(function(l){return!(l.type&
-(n|t))});if(q(c.next())&&!b.fill)b.fill=true;else c.m();if(r(c.next())){h++;j=c.R(function(){return!(d.type&(n|t|p))&&!(d.type&m&&d.value==="auto")});if(r(c.next())){h++;g=c.R(function(){return!(d.type&(n|p))})}}else c.m()};d=c.next();){a=d.type;e=d.value;if(a&(n|t)&&!f){c.m();w()}else if(q(d)&&!b.fill){b.fill=true;w()}else if(a&m&&this.qa[e]&&!b.repeat){b.repeat={i:e};if(d=c.next())if(d.type&m&&this.qa[d.value])b.repeat.Ha=d.value;else c.m()}else if(a&o.URL&&!b.src)b.src=e;else return null}if(!b.src||
-!f||f.length<1||f.length>4||j&&j.length>4||h===1&&j.length<1||g&&g.length>4||h===2&&g.length<1)return null;if(!b.repeat)b.repeat={i:"stretch"};if(!b.repeat.Ha)b.repeat.Ha=b.repeat.i;a=function(l,u){return{Q:u(l[0]),O:u(l[1]||l[0]),H:u(l[2]||l[0]),K:u(l[3]||l[1]||l[0])}};b.slice=a(f,function(l){return new D.g(l.type&n?l.value+"px":l.value)});b.width=j&&j.length>0?a(j,function(l){return l.type&(p|t)?new D.g(l.value):l.value}):(k=this.element.currentStyle)&&{Q:new D.g(k.borderTopWidth),O:new D.g(k.borderRightWidth),
-H:new D.g(k.borderBottomWidth),K:new D.g(k.borderLeftWidth)};b.ca=a(g||[0],function(l){return l.type&p?new D.g(l.value):l.value})}return b}});D.tb=D.w.M({aa:"box-shadow",da:"boxShadow",N:function(a){var b,c=D.g,d=D.k.Y,e;if(a){e=new D.k(a);b={ca:[],pa:[]};for(a=function(){for(var f,j,g,h,k,o;f=e.next();){g=f.value;j=f.type;if(j&d.X&&g===",")break;else if(f.Ea()&&!k){e.m();k=e.R(function(m){return!m.Ea()})}else if(j&d.S&&!h)h=g;else if(j&d.V&&g==="inset"&&!o)o=true;else return false}f=k&&k.length;
-if(f>1&&f<5){(o?b.pa:b.ca).push({oc:new c(k[0].value),qc:new c(k[1].value),blur:new c(k[2]?k[2].value:"0"),gc:new c(k[3]?k[3].value:"0"),color:new D.U(h||"currentColor")});return true}return false};a(););}return b&&(b.pa.length||b.ca.length)?b:null}});D.xb=D.w.M({ba:function(){var a=this.element.currentStyle;return a.visibility+"|"+a.display},N:function(){var a=this.element,b=a.runtimeStyle;a=a.currentStyle;var c=b.visibility,d;b.visibility="";d=a.visibility;b.visibility=c;return{lc:d!=="hidden",
-Qb:a.display!=="none"}},d:function(){return false}});D.p={L:function(a){function b(c,d,e){this.element=c;this.e=d;this.parent=e}D.q.Fa(b.prototype,D.p,a);return b},B:function(){return false},D:i(),cb:i(),u:i(),va:function(a,b){this.ab(a);for(var c=this.Z||(this.Z=[]),d=a+1,e=c.length,f;d<e;d++)if(f=c[d])break;c[a]=b;this.o().insertBefore(b,f||null)},ma:function(a){var b=this.Z;return b&&b[a]||null},ab:function(a){var b=this.ma(a),c=this.G;if(b&&c){c.removeChild(b);this.Z[a]=null}},na:function(a,b,
-c,d){var e=this.ta||(this.ta={}),f=e[a];if(!f){f=e[a]=D.q.ja("shape");if(b)f.appendChild(f[b]=D.q.ja(b));if(d){c=this.ma(d);if(!c){this.va(d,this.element.document.createElement("group"+d));c=this.ma(d)}}c.appendChild(f);a=f.style;a.position="absolute";a.left=a.top=0;a.behavior="url(#default#VML)"}return f},xa:function(a){var b=this.ta,c=b&&b[a];if(c){c.parentNode.removeChild(c);delete b[a]}return!!c},Wa:function(a){var b=this.element,c=b.offsetWidth,d=b.offsetHeight,e,f,j,g,h,k,o;e=a.x.tl.a(b,c);
-f=a.y.tl.a(b,d);j=a.x.tr.a(b,c);g=a.y.tr.a(b,d);h=a.x.br.a(b,c);k=a.y.br.a(b,d);o=a.x.bl.a(b,c);a=a.y.bl.a(b,d);c=Math.min(c/(e+j),d/(g+k),c/(o+h),d/(f+a));if(c<1){e*=c;f*=c;j*=c;g*=c;h*=c;k*=c;o*=c;a*=c}return{x:{tl:e,tr:j,br:h,bl:o},y:{tl:f,tr:g,br:k,bl:a}}},la:function(a,b,c){b=b||1;var d,e,f=this.element;e=f.offsetWidth*b;f=f.offsetHeight*b;var j=this.e.s,g=Math.floor,h=Math.ceil,k=a?a.Q*b:0,o=a?a.O*b:0,m=a?a.H*b:0;a=a?a.K*b:0;var n,p,t,r,q;if(c||j.d()){d=this.Wa(c||j.f());c=d.x.tl*b;j=d.y.tl*
-b;n=d.x.tr*b;p=d.y.tr*b;t=d.x.br*b;r=d.y.br*b;q=d.x.bl*b;b=d.y.bl*b;e="m"+g(a)+","+g(j)+"qy"+g(c)+","+g(k)+"l"+h(e-n)+","+g(k)+"qx"+h(e-o)+","+g(p)+"l"+h(e-o)+","+h(f-r)+"qy"+h(e-t)+","+h(f-m)+"l"+g(q)+","+h(f-m)+"qx"+g(a)+","+h(f-b)+" x e"}else e="m"+g(a)+","+g(k)+"l"+h(e-o)+","+g(k)+"l"+h(e-o)+","+h(f-m)+"l"+g(a)+","+h(f-m)+"xe";return e},o:function(){var a=this.parent.ma(this.zIndex),b;if(!a){a=this.element.document.createElement(this.ia);b=a.style;b.position="absolute";b.top=b.left=0;this.parent.va(this.zIndex,
-a)}return a},h:function(){this.parent.ab(this.zIndex);delete this.ta;delete this.Z}};D.vb=D.p.L({d:function(){var a=this.e;for(var b in a)if(a.hasOwnProperty(b)&&a[b].d())return true;return false},B:function(){return this.e.eb.j()},cb:function(){if(this.d()){var a=this.element,b=a,c,d,e=this.o().style,f=0;c=0;do b=b.offsetParent;while(b&&b.currentStyle.position==="static");c=a.getBoundingClientRect();if(b){d=b.getBoundingClientRect();b=b.currentStyle;f=c.left-d.left-(parseFloat(b.borderLeftWidth)||
-0);c=c.top-d.top-(parseFloat(b.borderTopWidth)||0)}else{b=a.document.documentElement;f=c.left+b.scrollLeft-b.clientLeft;c=c.top+b.scrollTop-b.clientTop}e.left=f;e.top=c;e.zIndex=a.currentStyle.position==="static"?-1:a.currentStyle.zIndex}},u:i(),db:function(){var a=this.e.eb.f();this.o().style.display=a.lc&&a.Qb?"":"none"},D:function(){this.d()?this.db():this.h()},o:function(){var a=this.G,b,c;if(!a){b=this.element;a=this.G=b.document.createElement("css3-container");c=a.style;c.position=b.currentStyle.position===
-"fixed"?"fixed":"absolute";this.db();b.parentNode.insertBefore(a,b)}return a},h:function(){var a=this.G;a&&a.parentNode&&a.parentNode.removeChild(a);delete this.G;delete this.Z}});D.lb=D.p.L({zIndex:2,ia:"background",B:function(){var a=this.e;return a.I.j()||a.s.j()},d:function(){var a=this.e,b=this.element;return b.offsetWidth&&b.offsetHeight&&(a.z.d()||a.s.d()||a.I.d()||a.A.d()&&a.A.f().pa)},u:function(){this.d()&&this.Sa()},D:function(){this.h();this.d()&&this.Sa()},Sa:function(){this.Rb();this.Sb()},
-Rb:function(){var a=this.e.I.f(),b=this.element,c=a&&a.color&&a.color.value(b),d,e,f;if(c&&c!=="transparent"){this.Xa();d=this.na("bgColor","fill",this.o(),1);e=b.offsetWidth;b=b.offsetHeight;d.stroked=false;d.coordsize=e*2+","+b*2;d.coordorigin="1,1";d.path=this.la(null,2);f=d.style;f.width=e;f.height=b;d.fill.color=c;a=a.color.Ra();if(a<1)d.fill.opacity=a}else this.xa("bgColor")},Sb:function(){var a=this.e.I.f();a=a&&a.images;var b,c,d,e,f,j;if(a){this.Xa();b=this.element;d=b.offsetWidth;e=b.offsetHeight;
-for(j=a.length;j--;){b=a[j];c=this.na("bgImage"+j,"fill",this.o(),2);c.stroked=false;c.fill.type="tile";c.fillcolor="none";c.coordsize=d*2+","+e*2;c.coordorigin="1,1";c.path=this.la(0,2);f=c.style;f.width=d;f.height=e;if(b.type==="linear-gradient")this.Hb(c,b);else{c.fill.src=b.url;this.cc(c,j)}}}for(j=a?a.length:0;this.xa("bgImage"+j++););},cc:function(a,b){D.q.ib(a.fill.src,function(c){var d=a.fill,e=this.element,f=e.offsetWidth,j=e.offsetHeight,g=this.e,h=g.$.f(),k=h&&h.fb;h=k?k.t.a(e):0;var o=
-k?k.r.a(e):0,m=k?k.b.a(e):0;k=k?k.l.a(e):0;g=g.I.f().images[b];e=g.position?g.position.coords(e,f-c.v-k-o,j-c.i-h-m):{x:0,y:0};g=g.repeat;m=o=0;var n=f+1,p=j+1,t=D.Da?0:1;k=e.x+k+0.5;h=e.y+h+0.5;d.position=k/f+","+h/j;if(g&&g!=="repeat"){if(g==="repeat-x"||g==="no-repeat"){o=h+1;p=h+c.i+t}if(g==="repeat-y"||g==="no-repeat"){m=k+1;n=k+c.v+t}a.style.clip="rect("+o+"px,"+n+"px,"+p+"px,"+m+"px)"}},this)},Hb:function(a,b){function c(x,y,v,C,G){if(v===0||v===180)return[C,y];else if(v===90||v===270)return[x,
-G];else{v=Math.tan(-v*m/180);x=v*x-y;y=-1/v;C=y*C-G;G=y-v;return[(C-x)/G,(v*C-y*x)/G]}}function d(){q=h>=90&&h<270?j:0;w=h<180?g:0;l=j-q;u=g-w}function e(x,y){var v=y[0]-x[0];x=y[1]-x[1];return Math.abs(v===0?x:x===0?v:Math.sqrt(v*v+x*x))}var f=this.element,j=f.offsetWidth,g=f.offsetHeight;a=a.fill;var h=b.wa,k=b.Ba;b=b.P;var o=b.length,m=Math.PI,n,p,t,r,q,w,l,u,s,z,B,A;if(k){k=k.coords(f,j,g);n=k.x;p=k.y}if(h){h=h.Pb();if(h<0)h+=360;h%=360;d();if(!k){n=q;p=w}k=c(n,p,h,l,u);t=k[0];r=k[1]}else if(k){t=
-j-n;r=g-p}else{n=p=t=0;r=g}k=t-n;s=r-p;if(h===undefined){h=-Math.atan2(s,k)/m*180;if(h<0)h+=360;h%=360;d()}k=Math.atan2(k*j/g,s)/m*180;k+=180;k%=360;z=e([n,p],[t,r]);t=e([q,w],c(q,w,h,l,u));r=[];p=e([n,p],c(n,p,h,q,w))/t*100;n=[];for(s=0;s<o;s++)n.push(b[s].Za?b[s].Za.a(f,z):s===0?0:s===o-1?z:null);for(s=1;s<o;s++){if(n[s]===null){B=n[s-1];z=s;do A=n[++z];while(A===null);n[s]=B+(A-B)/(z-s+1)}n[s]=Math.max(n[s],n[s-1])}for(s=0;s<o;s++)r.push(p+n[s]/t*100+"% "+b[s].color.value(f));a.angle=k;a.type=
-"gradient";a.method="sigma";a.color=b[0].color.value(f);a.color2=b[o-1].color.value(f);a.colors.value=r.join(",")},Xa:function(){var a=this.element.runtimeStyle;a.backgroundImage="url(about:blank)";a.backgroundColor="transparent"},h:function(){D.p.h.call(this);var a=this.element.runtimeStyle;a.backgroundImage=a.backgroundColor=""}});D.pb=D.p.L({zIndex:4,ia:"border",B:function(){var a=this.e;return a.$.j()||a.s.j()},d:function(){var a=this.e;return a.z.d()||a.s.d()||a.I.d()},u:function(){this.d()&&
-this.Ta()},D:function(){this.h();this.d()&&this.Ta()},Ta:function(){var a=this.element,b=a.offsetWidth,c=a.offsetHeight,d,e,f,j,g,h;if(this.e.$.f()){this.Xb();f=this.Ub(2);g=0;for(h=f.length;g<h;g++){j=f[g];d=this.na("borderPiece"+g,j.stroke?"stroke":"fill",this.o());d.coordsize=b*2+","+c*2;d.coordorigin="1,1";d.path=j.path;e=d.style;e.width=b;e.height=c;d.filled=!!j.fill;d.stroked=!!j.stroke;if(j.stroke){d=d.stroke;d.weight=j.Ia+"px";d.color=j.color.value(a);d.dashstyle=j.stroke==="dashed"?"2 2":
-j.stroke==="dotted"?"1 1":"solid";d.linestyle=j.stroke==="double"&&j.Ia>2?"ThinThin":"Single"}else d.fill.color=j.fill.value(a)}for(;this.xa("borderPiece"+g++););}},Xb:function(){var a=this.element,b=a.currentStyle,c=a.runtimeStyle,d=a.tagName,e;if(d==="BUTTON"||d==="INPUT"&&a.type in{submit:1,button:1,reset:1}){c.borderWidth="";a=this.e.$.bb;for(e=a.length;e--;){d=a[e];c["padding"+d]="";c["padding"+d]=parseInt(b["padding"+d])+parseInt(b["border"+d+"Width"])+(!D.Da&&e%2?1:0)}c.borderWidth=0}else if(D.Yb){if(a.childNodes.length!==
-1||a.firstChild.tagName!=="ie6-mask"){b=a.document.createElement("ie6-mask");d=b.style;d.visibility="visible";for(d.zoom=1;d=a.firstChild;)b.appendChild(d);a.appendChild(b);c.visibility="hidden"}}else c.borderColor="transparent"},Ub:function(a){var b=this.element,c,d,e=this.e.$,f=[],j,g,h,k,o,m,n,p;if(e.d()){e=e.f();m=e.fb;n=e.ic;p=e.Mb;if(e.nc&&e.jc&&e.Nb){e=m.t.a(b);h=e/2;f.push({path:this.la({Q:h,O:h,H:h,K:h},a),stroke:n.t,color:p.t,Ia:e})}else{a=a||1;c=b.offsetWidth;d=b.offsetHeight;e=m.t.a(b);
-h=m.r.a(b);k=m.b.a(b);b=m.l.a(b);var t={t:e,r:h,b:k,l:b};b=this.e.s;if(b.d())o=this.Wa(b.f());j=Math.floor;g=Math.ceil;var r=function(l,u){return o?o[l][u]:0},q=function(l,u,s,z,B,A){var x=r("x",l),y=r("y",l),v=l.charAt(1)==="r";l=l.charAt(0)==="b";return x>0&&y>0?(A?"al":"ae")+(v?g(c-x):j(x))*a+","+(l?g(d-y):j(y))*a+","+(j(x)-u)*a+","+(j(y)-s)*a+","+z*65535+","+2949075*(B?1:-1):(A?"m":"l")+(v?c-u:u)*a+","+(l?d-s:s)*a},w=function(l,u,s,z){var B=l==="t"?j(r("x","tl"))*a+","+g(u)*a:l==="r"?g(c-u)*a+
-","+j(r("y","tr"))*a:l==="b"?g(c-r("x","br"))*a+","+j(d-u)*a:j(u)*a+","+g(d-r("y","bl"))*a;l=l==="t"?g(c-r("x","tr"))*a+","+g(u)*a:l==="r"?g(c-u)*a+","+g(d-r("y","br"))*a:l==="b"?j(r("x","bl"))*a+","+j(d-u)*a:j(u)*a+","+j(r("y","tl"))*a;return s?(z?"m"+l:"")+"l"+B:(z?"m"+B:"")+"l"+l};b=function(l,u,s,z,B,A){var x=l==="l"||l==="r",y=t[l],v,C;if(y>0&&n[l]!=="none"){v=t[x?l:u];u=t[x?u:l];C=t[x?l:s];s=t[x?s:l];if(n[l]==="dashed"||n[l]==="dotted"){f.push({path:q(z,v,u,A+45,0,1)+q(z,0,0,A,1,0),fill:p[l]});
-f.push({path:w(l,y/2,0,1),stroke:n[l],Ia:y,color:p[l]});f.push({path:q(B,C,s,A,0,1)+q(B,0,0,A-45,1,0),fill:p[l]})}else f.push({path:q(z,v,u,A+45,0,1)+w(l,y,0,0)+q(B,C,s,A,0,0)+(n[l]==="double"&&y>2?q(B,C-j(C/3),s-j(s/3),A-45,1,0)+w(l,g(y/3*2),1,0)+q(z,v-j(v/3),u-j(u/3),A,1,0)+"x "+q(z,j(v/3),j(u/3),A+45,0,1)+w(l,j(y/3),1,0)+q(B,j(C/3),j(s/3),A,0,0):"")+q(B,0,0,A-45,1,0)+w(l,0,1,0)+q(z,0,0,A,1,0),fill:p[l]})}};b("t","l","r","tl","tr",90);b("r","t","b","tr","br",0);b("b","r","l","br","bl",-90);b("l",
-"b","t","bl","tl",-180)}}return f},h:function(){D.p.h.call(this);this.element.runtimeStyle.borderColor=""}});D.nb=D.p.L({zIndex:5,bc:["t","tr","r","br","b","bl","l","tl","c"],B:function(){var a=this.e;return a.z.j()||a.z.j()},d:function(){return this.e.z.d()},u:function(){if(this.d()){var a=this.e.z.f();this.o();var b=this.element,c=this.$a;D.q.ib(a.src,function(d){function e(q,w,l,u,s){q=c[q].style;q.width=w;q.height=l;q.left=u;q.top=s}function f(q,w,l){for(var u=0,s=q.length;u<s;u++)c[q[u]].imagedata[w]=
-l}var j=b.offsetWidth,g=b.offsetHeight,h=a.width,k=h.Q.a(b),o=h.O.a(b),m=h.H.a(b);h=h.K.a(b);var n=a.slice,p=n.Q.a(b),t=n.O.a(b),r=n.H.a(b);n=n.K.a(b);e("tl",h,k,0,0);e("t",j-h-o,k,h,0);e("tr",o,k,j-o,0);e("r",o,g-k-m,j-o,k);e("br",o,m,j-o,g-m);e("b",j-h-o,m,h,g-m);e("bl",h,m,0,g-m);e("l",h,g-k-m,0,k);e("c",j-h-o,g-k-m,h,k);f(["tl","t","tr"],"cropBottom",(d.i-p)/d.i);f(["tl","l","bl"],"cropRight",(d.v-n)/d.v);f(["bl","b","br"],"cropTop",(d.i-r)/d.i);f(["tr","r","br"],"cropLeft",(d.v-t)/d.v);if(a.repeat.Ha===
-"stretch"){f(["l","r","c"],"cropTop",p/d.i);f(["l","r","c"],"cropBottom",r/d.i)}if(a.repeat.i==="stretch"){f(["t","b","c"],"cropLeft",n/d.v);f(["t","b","c"],"cropRight",t/d.v)}c.c.style.display=a.fill?"":"none"},this)}else this.h()},D:function(){this.h();this.d()&&this.u()},o:function(){var a=this.G,b,c,d,e=this.bc,f=e.length;if(!a){a=this.G=this.element.document.createElement("border-image");b=a.style;b.position="absolute";this.$a={};for(d=0;d<f;d++){c=this.$a[e[d]]=D.q.ja("rect");c.appendChild(D.q.ja("imagedata"));
-b=c.style;b.behavior="url(#default#VML)";b.position="absolute";b.top=b.left=0;c.imagedata.src=this.e.z.f().src;c.stroked=false;c.filled=false;a.appendChild(c)}this.parent.va(this.zIndex,a)}return a}});D.sb=D.p.L({zIndex:1,ia:"outset-box-shadow",B:function(){var a=this.e;return a.A.j()||a.s.j()},d:function(){var a=this.e.A;return a.d()&&a.f().ca[0]},u:function(){if(this.d()){var a=this,b=this.element,c=this.o(),d=this.e,e=d.A.f().ca;d=d.s.f();for(var f=e.length,j=f,g,h=b.offsetWidth,k=b.offsetHeight,
-o=D.Da?1:0,m=["tl","tr","br","bl"],n,p,t,r,q,w,l,u,s,z,B,A,x,y=function(v,C,G,O,P,Q,R){v=a.na("shadow"+v+C,"fill",c,f-v);C=v.style;var I=v.fill;C.left=G;C.top=O;v.coordsize=h*2+","+k*2;v.coordorigin="1,1";v.stroked=false;v.filled=true;I.color=P.value(b);if(Q){I.type="gradienttitle";I.color2=I.color;I.opacity=0}v.path=R;C.width=h;C.height=k;return v};j--;){p=e[j];r=p.oc.a(b);q=p.qc.a(b);g=p.gc.a(b);w=p.blur.a(b);p=p.color;l=-g-w;if(!d&&w)d=D.sa.jb;l=this.la({Q:l,O:l,H:l,K:l},2,d);if(w){u=(g+w)*2+h;
-s=(g+w)*2+k;z=w*2/u;B=w*2/s;if(w-g>h/2||w-g>k/2)for(g=4;g--;){n=m[g];A=n.charAt(0)==="b";x=n.charAt(1)==="r";n=y(j,n,r,q,p,w,l);t=n.fill;t.focusposition=(x?1-z:z)+","+(A?1-B:B);t.focussize="0,0";n.style.clip="rect("+((A?s/2:0)+o)+"px,"+(x?u:u/2)+"px,"+(A?s:s/2)+"px,"+((x?u/2:0)+o)+"px)"}else{n=y(j,"",r,q,p,w,l);t=n.fill;t.focusposition=z+","+B;t.focussize=1-z*2+","+(1-B*2)}}else{n=y(j,"",r,q,p,w,l);r=p.Ra();if(r<1)n.fill.opacity=r}}}else this.h()},D:function(){this.h();this.u()}});D.rb=D.p.L({zIndex:3,
-ia:"inset-box-shadow",B:function(){var a=this.e;return a.A.j()||a.s.j()},d:function(){var a=this.e.A;return a.d()&&a.f().pa[0]},u:i(),D:i()})}var E,F,H,J,K,L,M;function update(){init();var a=element.getBoundingClientRect(),b=a.left,c=a.top,d=a.right-b;a=a.bottom-c;var e,f;if(b!==H||c!==J){e=0;for(f=K.length;e<f;e++)K[e].cb();H=b;J=c}if(d!==E||a!==F){e=0;for(f=K.length;e<f;e++)K[e].u();E=d;F=a}}
-function propChanged(){init();var a,b,c=[];a=0;for(b=K.length;a<b;a++)K[a].B()&&c.push(K[a]);a=0;for(b=c.length;a<b;a++)c[a].D()}function mouseEntered(){event.srcElement.className+=" "+D.Ka+"hover";setTimeout(propChanged,0)}function mouseLeft(){var a=event.srcElement;a.className=a.className.replace(new RegExp("\\b"+D.Ka+"hover\\b","g"),"");setTimeout(propChanged,0)}function N(){var a=event.propertyName;if(a==="className"||a==="id")propChanged()}
-function cleanup(){var a,b;if(K){a=0;for(b=K.length;a<b;a++)K[a].h();K=null}L=null;if(M){a=0;for(b=M.length;a<b;a++){M[a].detachEvent("onpropertychange",N);M[a].detachEvent("onmouseenter",mouseEntered);M[a].detachEvent("onmouseleave",mouseLeft)}M=null}D.oa===8&&D.Ca.remove(update)}
-function init(){if(!K){var a=element;a.runtimeStyle.zoom=1;L={I:new D.mb(a),$:new D.qb(a),z:new D.ob(a),s:new D.sa(a),A:new D.tb(a),eb:new D.xb(a)};var b=new D.vb(a,L);K=[b,new D.sb(a,L,b),new D.lb(a,L,b),new D.rb(a,L,b),new D.pb(a,L,b),new D.nb(a,L,b)];var c=element;if(a=c.currentStyle.getAttribute(D.T+"watch-ancestors")){M=[];a=parseInt(a,10);b=0;for(c=c.parentNode;c&&(a==="NaN"||b++<a);){M.push(c);c.attachEvent("onpropertychange",N);c.attachEvent("onmouseenter",mouseEntered);c.attachEvent("onmouseleave",
-mouseLeft);c=c.parentNode}}D.oa===8&&D.Ca.add(update)}}element.readyState==="complete"&&update();
-    </script>
-
-</PUBLIC:COMPONENT>
index 408a541..a083eec 100644 (file)
@@ -73,6 +73,3 @@ jQuery(document).ready(function(){
     jQuery("#prefs-menu").addClass("sf-menu sf-js-enabled").supersubs().superfish().supposition({ speed: 'fast' });
 });
 </script>
     jQuery("#prefs-menu").addClass("sf-menu sf-js-enabled").supersubs().superfish().supposition({ speed: 'fast' });
 });
 </script>
-<!--[if (lt IE 9)&(gt IE 6)]>
-<link rel="stylesheet" href="<%RT->Config->Get('WebPath')%>/NoAuth/css/web2/msie-pie.css" type="text/css" media="all" />
-<![endif]-->
diff --git a/rt/share/html/NoAuth/css/web2/msie-pie.css b/rt/share/html/NoAuth/css/web2/msie-pie.css
deleted file mode 100644 (file)
index 73d76d0..0000000
+++ /dev/null
@@ -1,60 +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 }}}
-.search-result-views,
-.ticket-transaction div.metadata span.actions,
-div#ticket-history div.downloadattachment, 
-.ticket-transaction div.metadata  span.type,
-.titlebox,
-.titlebox .titlebox-title .right,
-.titlebox .titlebox-title .left,
-div#footer,
-#main-navigation,
-#page-navigation,
-div#body {
-    behavior: url(<%RT->Config->Get('WebPath')%>/NoAuth/css/images/PIE.htc);
-}
index 5199637..a5e5dac 100644 (file)
@@ -46,7 +46,7 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 function createCookie(name,value,days) {
 %#
 %# END BPS TAGGED BLOCK }}}
 function createCookie(name,value,days) {
-    var path = "<%RT->Config->Get('WebPath')%>" ? "<%RT->Config->Get('WebPath')%>" : "/";
+    var path = <%RT->Config->Get('WebPath')|n,j%> ? <%RT->Config->Get('WebPath')|n,j%> : "/";
 
     if (days) {
         var date = new Date();
 
     if (days) {
         var date = new Date();
index db244d1..b4f678c 100644 (file)
@@ -70,7 +70,7 @@ jQuery(function() {
             continue;
 
         var options = {
             continue;
 
         var options = {
-            source: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Users"
+            source: <% RT->Config->Get('WebPath') |n,j%>+"/Helpers/Autocomplete/Users"
         };
 
         var queryargs = [];
         };
 
         var queryargs = [];
index 62ee922..5bfce41 100644 (file)
@@ -294,8 +294,8 @@ function ReplaceAllTextareas(encoded) {
             textArea.parentNode.appendChild(typeField);
 
 
             textArea.parentNode.appendChild(typeField);
 
 
-            CKEDITOR.replace(textArea.name,{width:'100%',height:'<% RT->Config->Get('MessageBoxRichTextHeight') %>'});
-            CKEDITOR.basePath = "<%RT->Config->Get('WebPath')%>/NoAuth/RichText/";
+            CKEDITOR.replace(textArea.name,{width:'100%',height:<% RT->Config->Get('MessageBoxRichTextHeight') |n,j%>});
+            CKEDITOR.basePath = <%RT->Config->Get('WebPath')|n,j%>+"/NoAuth/RichText/";
 
             jQuery("#" + textArea.name + "___Frame").addClass("richtext-editor");
         }
 
             jQuery("#" + textArea.name + "___Frame").addClass("richtext-editor");
         }
index fdd9c17..42aa16b 100644 (file)
@@ -95,7 +95,7 @@ $ARGS{'OrderBy'} = join '|', grep defined && /\S/, (ref $ARGS{'OrderBy'})? @{$AR
 my ( $AvailableColumns, $CurrentFormat );
 ( $ARGS{Format}, $AvailableColumns, $CurrentFormat ) = $m->comp(
     '/Search/Elements/BuildFormatString',
 my ( $AvailableColumns, $CurrentFormat );
 ( $ARGS{Format}, $AvailableColumns, $CurrentFormat ) = $m->comp(
     '/Search/Elements/BuildFormatString',
-    cfqueues => {}, %ARGS
+    %ARGS
 );
 
 if ($ARGS{'Save'}) {
 );
 
 if ($ARGS{'Save'}) {
index 9ae803d..9a2212b 100755 (executable)
@@ -149,10 +149,16 @@ else {
             }
             # Set custom field
             elsif ($k =~ /^$cf_spec/) {
             }
             # Set custom field
             elsif ($k =~ /^$cf_spec/) {
-                my $cf = RT::CustomField->new( RT->SystemUser );
-                my $cfk = $1 || $2;
-                unless($cf->LoadByName( Name => $cfk )) {
-                    push @comments, "# Invalid custom field name ($cfk)";
+                my $key = $1 || $2;
+
+                my $cf = RT::CustomField->new( $session{CurrentUser} );
+                $cf->LoadByName( Name => $key, Queue => $data{Queue} || $v{Queue} );
+                unless ( $cf->id ) {
+                    $cf->LoadByName( Name => $key, Queue => 0 );
+                }
+
+                if (not $cf->id) {
+                    push @comments, "# Invalid custom field name ($key)";
                     delete $data{$k};
                     next;
                 }
                     delete $data{$k};
                     next;
                 }
@@ -348,9 +354,15 @@ else {
         }
         # Set custom field
         elsif ($key =~ /^$cf_spec/) {
         }
         # Set custom field
         elsif ($key =~ /^$cf_spec/) {
-            my $cf = RT::CustomField->new( RT->SystemUser );
             $key = $1 || $2;
             $key = $1 || $2;
-            if (not $cf->LoadByName( Name => $key )) {
+
+            my $cf = RT::CustomField->new( $session{CurrentUser} );
+            $cf->LoadByName( Name => $key, Queue => $ticket->Queue );
+            unless ( $cf->id ) {
+                $cf->LoadByName( Name => $key, Queue => 0 );
+            }
+
+            if (not $cf->id) {
                 $n = 0;
                 $s = "Unknown custom field.";
             }
                 $n = 0;
                 $s = "Unknown custom field.";
             }
index e2e1830..bf4f257 100755 (executable)
@@ -100,7 +100,8 @@ if ($changes) {
                     my $tick = RT::Ticket->new($session{CurrentUser});
                     $tick->Load($nkey);
                     if ($tick->Id) {
                     my $tick = RT::Ticket->new($session{CurrentUser});
                     $tick->Load($nkey);
                     if ($tick->Id) {
-                        $nkey = $uri->FromObject($tick);
+                        $uri->FromObject($tick);
+                        $nkey = $uri->URI;
                     }
                     else {
                         $n = 0;
                     }
                     else {
                         $n = 0;
index 1ffa2b2..2e45f67 100644 (file)
@@ -49,7 +49,6 @@
 %#
 <%ARGS>
 $id
 %#
 <%ARGS>
 $id
-$args => undef
 $format => undef
 $fields => undef
 </%ARGS>
 $format => undef
 $fields => undef
 </%ARGS>
@@ -57,8 +56,6 @@ $fields => undef
 my $trans = RT::Transactions->new($session{CurrentUser});
 my ($c, $o, $k, $e) = ("", [], {} , "");
 
 my $trans = RT::Transactions->new($session{CurrentUser});
 my ($c, $o, $k, $e) = ("", [], {} , "");
 
-chomp $args;
-my @arglist = split('/', $args);
 my $tid = $id;
 
 $trans->Limit(FIELD => 'Id', OPERATOR => '=', VALUE => $tid);
 my $tid = $id;
 
 $trans->Limit(FIELD => 'Id', OPERATOR => '=', VALUE => $tid);
index aa80b0d..8d3345f 100755 (executable)
@@ -81,10 +81,9 @@ if ($id && $object && $id != $object) {
     goto OUTPUT;
 }
 $id ||= $object;
     goto OUTPUT;
 }
 $id ||= $object;
-unless ($id =~ /^\d+$/ && $to =~ /^\d+$/) {
-    my $bad = ($id !~ /^\d+$/) ? $id : $to;
+unless ($id =~ /^\d+$/) {
     $output = $r->path_info. "\n";
     $output = $r->path_info. "\n";
-    $output .= "Invalid ticket id: '$bad'.\n";
+    $output .= "Invalid ticket id: '$id'.\n";
     $status = "400 Bad Request";
     goto OUTPUT;
 }
     $status = "400 Bad Request";
     goto OUTPUT;
 }
index ae4c7ba..b200f90 100644 (file)
@@ -78,7 +78,7 @@
 
 
 <div id="pick-criteria">
 
 
 <div id="pick-criteria">
-    <& Elements/PickCriteria, query => $query{'Query'}, cfqueues => $queues &>
+    <& Elements/PickCriteria, query => $query{'Query'}, queues => $queues &>
 <& /Elements/Submit,  Label => loc('Add these terms'), SubmitId => 'AddClause', Name => 'AddClause'&>
 <& /Elements/Submit, Label => loc('Add these terms and Search'), SubmitId => 'DoSearch', Name => 'DoSearch'&>
 </div>
 <& /Elements/Submit,  Label => loc('Add these terms'), SubmitId => 'AddClause', Name => 'AddClause'&>
 <& /Elements/Submit, Label => loc('Add these terms and Search'), SubmitId => 'DoSearch', Name => 'DoSearch'&>
 </div>
@@ -275,7 +275,7 @@ my ( $AvailableColumns, $CurrentFormat );
 ( $query{'Format'}, $AvailableColumns, $CurrentFormat ) = $m->comp(
     'Elements/BuildFormatString',
     %ARGS,
 ( $query{'Format'}, $AvailableColumns, $CurrentFormat ) = $m->comp(
     'Elements/BuildFormatString',
     %ARGS,
-    cfqueues => $queues,
+    queues => $queues,
     Format => $query{'Format'},
 );
 
     Format => $query{'Format'},
 );
 
@@ -308,7 +308,7 @@ if ( $ARGS{'DoSearch'} ) {
         SavedChartSearchId => $ARGS{'SavedChartSearchId'},
         SavedSearchId => $saved_search{'Id'},
     );
         SavedChartSearchId => $ARGS{'SavedChartSearchId'},
         SavedSearchId => $saved_search{'Id'},
     );
-    RT::Interface::Web::Redirect(RT->Config->Get('WebPath') . '/Search/Results.html?' . $redir_query_string);
+    RT::Interface::Web::Redirect(RT->Config->Get('WebURL') . 'Search/Results.html?' . $redir_query_string);
     $m->abort;
 }
 
     $m->abort;
 }
 
index 884d183..070ce7c 100644 (file)
@@ -124,7 +124,7 @@ my %query;
 <input type="hidden" class="hidden" name="Query" value="<% $ARGS{Query} %>" />
 <input type="hidden" class="hidden" name="SavedChartSearchId" value="<% $saved_search->{SearchId} || 'new' %>" />
 
 <input type="hidden" class="hidden" name="Query" value="<% $ARGS{Query} %>" />
 <input type="hidden" class="hidden" name="SavedChartSearchId" value="<% $saved_search->{SearchId} || 'new' %>" />
 
-<&|/l, $m->scomp('Elements/SelectChartType', Name => 'ChartStyle', Default => $ChartStyle), $m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $ARGS{Query}, Default => $PrimaryGroupBy) 
+<&|/l_unsafe, $m->scomp('Elements/SelectChartType', Name => 'ChartStyle', Default => $ChartStyle), $m->scomp('Elements/SelectGroupBy', Name => 'PrimaryGroupBy', Query => $ARGS{Query}, Default => $PrimaryGroupBy) 
 &>[_1] chart by [_2]</&><input type="submit" class="button" value="<%loc('Update Chart')%>" />
 </form>
 </&>
 &>[_1] chart by [_2]</&><input type="submit" class="button" value="<%loc('Update Chart')%>" />
 </form>
 </&>
index 3769972..a39287b 100644 (file)
@@ -48,7 +48,7 @@
 <%ARGS>
 $Format => RT->Config->Get('DefaultSearchResultFormat')
 
 <%ARGS>
 $Format => RT->Config->Get('DefaultSearchResultFormat')
 
-%cfqueues => ()
+%queues => ()
 
 $Face => undef
 $Size => undef
 
 $Face => undef
 $Size => undef
@@ -111,17 +111,11 @@ my @fields = (
 $m->callback( CallbackOnce => 1, CallbackName => 'SetFieldsOnce', Fields => \@fields );
 
 my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
 $m->callback( CallbackOnce => 1, CallbackName => 'SetFieldsOnce', Fields => \@fields );
 
 my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
-foreach my $id (keys %cfqueues) {
+foreach my $id (keys %queues) {
     # Gotta load up the $queue object, since queues get stored by name now. my $id
     my $queue = RT::Queue->new($session{'CurrentUser'});
     $queue->Load($id);
     # Gotta load up the $queue object, since queues get stored by name now. my $id
     my $queue = RT::Queue->new($session{'CurrentUser'});
     $queue->Load($id);
-    unless ($queue->id) {
-        # XXX TODO: This ancient code dates from a former developer
-        # we have no idea what it means or why cfqueues are so encoded.
-        $id =~ s/^.'*(.*).'*$/$1/;
-        $queue->Load($id);
-    }
-    $CustomFields->LimitToQueue($queue->Id);
+    $CustomFields->LimitToQueue($queue->Id) if $queue->Id;
 }
 $CustomFields->LimitToGlobal;
 
 }
 $CustomFields->LimitToGlobal;
 
index 01b78c7..be05da3 100644 (file)
@@ -130,10 +130,10 @@ my ($i,$total);
                          );
 </%perl>
 <td class="label collection-as-table">
                          );
 </%perl>
 <td class="label collection-as-table">
-<a href=<% RT->Config->Get('WebURL') %>Search/Results.html?<%$QueryString%>><%$key%></a>
+<a href=<% RT->Config->Get('WebPath') %>/Search/Results.html?<%$QueryString%>><%$key%></a>
 </td>
 <td class="value collection-as-table">
 </td>
 <td class="value collection-as-table">
-<a href=<% RT->Config->Get('WebURL') %>Search/Results.html?<%$QueryString%>><%$value%></a>
+<a href=<% RT->Config->Get('WebPath') %>/Search/Results.html?<%$QueryString%>><%$value%></a>
 </td>
 % } else {
 <td class="label collection-as-table"><% $key %></td>
 </td>
 % } else {
 <td class="label collection-as-table"><% $key %></td>
index 7223b75..db7d9f5 100644 (file)
@@ -103,7 +103,7 @@ my @lines = (
         Value => {
             Type => 'component',
             Path => '/Elements/SelectStatus',
         Value => {
             Type => 'component',
             Path => '/Elements/SelectStatus',
-            Arguments => { SkipDeleted => 1 },
+            Arguments => { SkipDeleted => 1, Queues => \%queues },
         },
     },
     {
         },
     },
     {
@@ -124,7 +124,7 @@ my @lines = (
         Value => {
             Type => 'component',
             Path => '/Elements/SelectOwner',
         Value => {
             Type => 'component',
             Path => '/Elements/SelectOwner',
-            Arguments => { ValueAttribute => 'Name' },
+            Arguments => { ValueAttribute => 'Name', Queues => \%queues },
         },
     },
     {
         },
     },
     {
@@ -214,3 +214,6 @@ my @lines = (
 $m->callback( Conditions => \@lines );
 
 </%INIT>
 $m->callback( Conditions => \@lines );
 
 </%INIT>
+<%ARGS>
+%queues => ()
+</%ARGS>
index 4b9a88b..f2dc21f 100644 (file)
 % }
 <%INIT>
 my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
 % }
 <%INIT>
 my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
-foreach my $id (keys %cfqueues) {
-    # Gotta load up the $queue object, since queues get stored by name now. my $id
+foreach my $id (keys %queues) {
+    # Gotta load up the $queue object, since queues get stored by name now.
     my $queue = RT::Queue->new($session{'CurrentUser'});
     $queue->Load($id);
     my $queue = RT::Queue->new($session{'CurrentUser'});
     $queue->Load($id);
-    unless ($queue->id) {
-        # XXX TODO: This ancient code dates from a former developer
-        # we have no idea what it means or why cfqueues are so encoded.
-        $id =~ s/^.'*(.*).'*$/$1/;
-
-        # unescape internal quotes
-        $id =~ s/(\\(.))/$2 eq "'" ? "'" : $1/eg;
-
-        $queue->Load($id);
-    }
-    $CustomFields->LimitToQueue($queue->Id);
+    $CustomFields->LimitToQueue($queue->Id) if $queue->Id;
 }
 $CustomFields->LimitToGlobal;
 $m->callback(
 }
 $CustomFields->LimitToGlobal;
 $m->callback(
@@ -124,10 +114,10 @@ while ( my $CustomField = $CustomFields->Next ) {
     push @lines, \%line;
 }
 
     push @lines, \%line;
 }
 
-$m->callback( Conditions => \@lines, Queues => \%cfqueues );
+$m->callback( Conditions => \@lines, Queues => \%queues );
 
 </%INIT>
 
 <%ARGS>
 
 </%INIT>
 
 <%ARGS>
-%cfqueues => undef
+%queues => ()
 </%ARGS>
 </%ARGS>
index 5d0b8af..74547c7 100644 (file)
@@ -53,7 +53,7 @@
 
 <& PickBasics &>
 <& PickCustomerFields &>
 
 <& PickBasics &>
 <& PickCustomerFields &>
-<& PickCFs, cfqueues => \%cfqueues &>
+<& PickCFs, queues => \%queues &>
 
 <tr class="separator"><td colspan="3"><hr /></td></tr>
 <tr>
 
 <tr class="separator"><td colspan="3"><hr /></td></tr>
 <tr>
@@ -69,5 +69,5 @@
 <%ARGS>
 $addquery => 0
 $query => undef
 <%ARGS>
 $addquery => 0
 $query => undef
-%cfqueues => undef
+%queues => ()
 </%ARGS>
 </%ARGS>
index 0040d2a..171b38d 100755 (executable)
@@ -46,7 +46,7 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <& /Elements/Header, Title => $title,
 %#
 %# END BPS TAGGED BLOCK }}}
 <& /Elements/Header, Title => $title,
-    Refresh => $session{'tickets_refresh_interval'} || RT->Config->Get('SearchResultsRefreshInterval', $session{'CurrentUser'} ),
+    Refresh => $refresh,
     LinkRel => \%link_rel &>
 <& /Elements/Tabs &>
 <& /Elements/CollectionList, 
     LinkRel => \%link_rel &>
 <& /Elements/Tabs &>
 <& /Elements/CollectionList, 
@@ -148,6 +148,16 @@ if ($ARGS{'TicketsRefreshInterval'}) {
        $session{'tickets_refresh_interval'} = $ARGS{'TicketsRefreshInterval'};
 }
 
        $session{'tickets_refresh_interval'} = $ARGS{'TicketsRefreshInterval'};
 }
 
+my $refresh = $session{'tickets_refresh_interval'}
+    || RT->Config->Get('SearchResultsRefreshInterval', $session{'CurrentUser'} );
+
+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')
+        . "Search/Results.html?CSRF_Token="
+            . $token;
+}
+
 my %link_rel;
 my $genpage = sub {
     return $m->comp(
 my %link_rel;
 my $genpage = sub {
     return $m->comp(
index 07bd2f4..4d7b1e3 100644 (file)
@@ -60,7 +60,7 @@
 
 % my @strong = qw(<strong> </strong>);
 
 
 % my @strong = qw(<strong> </strong>);
 
-<p><&|/l, @strong &>Search for tickets by entering [_1]id[_2] numbers, subject words [_1]"in quotes"[_2], [_1]queues[_2] by name, Owners by [_1]username[_2], Requestors by [_1]email address[_2], and ticket [_1]statuses[_2].</&></p>
+<p><&|/l_unsafe, @strong &>Search for tickets by entering [_1]id[_2] numbers, subject words [_1]"in quotes"[_2], [_1]queues[_2] by name, Owners by [_1]username[_2], Requestors by [_1]email address[_2], and ticket [_1]statuses[_2].</&></p>
 
 <p><&|/l&>Any word not recognized by RT is searched for in ticket subjects.</&></p>
 
 
 <p><&|/l&>Any word not recognized by RT is searched for in ticket subjects.</&></p>
 
@@ -74,7 +74,7 @@
 % }
 % }
 
 % }
 % }
 
-<p><&|/l, map { "<strong>$_</strong>" } qw(initial active inactive any) &>Entering [_1], [_2], [_3], or [_4] limits results to tickets with one of the respective types of statuses.  Any individual status name limits results to just the statuses named.</&>
+<p><&|/l_unsafe, map { "<strong>$_</strong>" } qw(initial active inactive any) &>Entering [_1], [_2], [_3], or [_4] limits results to tickets with one of the respective types of statuses.  Any individual status name limits results to just the statuses named.</&>
 
 % if (RT->Config->Get('OnlySearchActiveTicketsInSimpleSearch', $session{'CurrentUser'})) {
 % my $status_str  = join ', ', map { loc($_) } RT::Queue->ActiveStatusArray;
 
 % if (RT->Config->Get('OnlySearchActiveTicketsInSimpleSearch', $session{'CurrentUser'})) {
 % my $status_str  = join ', ', map { loc($_) } RT::Queue->ActiveStatusArray;
 % }
 </p>
 
 % }
 </p>
 
-<p><&|/l, map { "<strong>$_</strong>" } 'queue:"Example Queue"', 'owner:email@example.com' &>Start the search term with the name of a supported field followed by a colon, as in [_1] and [_2], to explicitly specify the search type.</&></p>
+<p><&|/l_unsafe, map { "<strong>$_</strong>" } 'queue:"Example Queue"', 'owner:email@example.com' &>Start the search term with the name of a supported field followed by a colon, as in [_1] and [_2], to explicitly specify the search type.</&></p>
 
 
-<p><&|/l, '<strong>cf.Name:value</strong>' &>CFs may be searched using a similar syntax as above with [_1].</&></p>
+<p><&|/l_unsafe, '<strong>cf.Name:value</strong>' &>CFs may be searched using a similar syntax as above with [_1].</&></p>
 
 % my $link_start  = '<a href="' . RT->Config->Get('WebPath') . '/Search/Build.html">';
 % my $link_end    = '</a>';
 
 % my $link_start  = '<a href="' . RT->Config->Get('WebPath') . '/Search/Build.html">';
 % my $link_end    = '</a>';
-<p><&|/l, $link_start, $link_end &>For the full power of RT's searches, please visit the [_1]search builder interface[_2].</&></p>
+<p><&|/l_unsafe, $link_start, $link_end &>For the full power of RT's searches, please visit the [_1]search builder interface[_2].</&></p>
 
 </form>
 
 
 </form>
 
index 549d458..76accfc 100755 (executable)
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<&| /Widgets/TitleBox, title =>  $title &>
+<&| /Widgets/TitleBox, title => $title &>
 <& /Elements/CollectionList, Title   => $title,
                         Format  => $Format, 
                         Query   => $Query, 
                         Order   => @Order, 
                         OrderBy => @OrderBy,
                         BaseURL => $BaseURL,
 <& /Elements/CollectionList, Title   => $title,
                         Format  => $Format, 
                         Query   => $Query, 
                         Order   => @Order, 
                         OrderBy => @OrderBy,
                         BaseURL => $BaseURL,
-                        GenericQueryArgs => $GenericQueryArgs,
-                        AllowSorting => $AllowSorting,
+                        AllowSorting => 1,
                         Class   => 'RT::Tickets',
              Rows    => $Rows,
                         Page    => $Page &>
 </&>
 
 <%INIT>
                         Class   => 'RT::Tickets',
              Rows    => $Rows,
                         Page    => $Page &>
 </&>
 
 <%INIT>
+my $title = loc("My [_1] tickets", $friendly_status);
 my $id = $session{'CurrentUser'}->id;
 my $id = $session{'CurrentUser'}->id;
-my $Query = "( "
-    . join( ' OR ', map "$_.id = $id", @roles )
-    . ")";
+my $Query = "( Watcher.id = $id )";
 if ( @status ) {
 if ( @status ) {
-    $Query .= " AND ( "
-        . join( ' OR ', map "Status = '$_'", @status )
-        . " )";
+    @status = map {s/(['\\])/\\$1/g; "Status = '$_'"} @status;
+    $Query .= " AND ( " . join(' OR ', @status ) . " )";
 }
 my $Format = RT->Config->Get('DefaultSelfServiceSearchResultFormat');
 }
 my $Format = RT->Config->Get('DefaultSelfServiceSearchResultFormat');
-
 </%INIT>
 <%ARGS>
 $friendly_status => loc('open')
 </%INIT>
 <%ARGS>
 $friendly_status => loc('open')
-$title => loc("My [_1] tickets", $friendly_status)
-@roles => ('Watcher')
-@status => RT::Queue->ActiveStatusArray()
+@status => ()
 $BaseURL => undef
 $Page => 1
 $BaseURL => undef
 $Page => 1
-$GenericQueryArgs => undef
-$AllowSorting => 1
 @Order => ('ASC')
 @OrderBy => ('Created')
 $Rows => 50
 @Order => ('ASC')
 @OrderBy => ('Created')
 $Rows => 50
index a89296b..29accf5 100755 (executable)
@@ -48,6 +48,8 @@
 <& /SelfService/Elements/Header, Title => loc('Open tickets') &>
 <& /SelfService/Elements/MyRequests,
     %ARGS,
 <& /SelfService/Elements/Header, Title => loc('Open tickets') &>
 <& /SelfService/Elements/MyRequests,
     %ARGS,
+    status          => [ RT::Queue->ActiveStatusArray() ],
+    friendly_status => loc('open'),
     BaseURL => RT->Config->Get('WebPath') ."/SelfService/?",
     Page    => $Page, 
 &>
     BaseURL => RT->Config->Get('WebPath') ."/SelfService/?",
     Page    => $Page, 
 &>
index 9fb2b2b..f29dfbb 100755 (executable)
       <& /Ticket/Elements/EditTransactionCustomFields, %ARGS, QueueObj => $QueueObj, InTable => 1 &>
     </table>
   </&>
       <& /Ticket/Elements/EditTransactionCustomFields, %ARGS, QueueObj => $QueueObj, InTable => 1 &>
     </table>
   </&>
-% $m->callback( CallbackName => 'AfterBasics', QueueObj => $QueueObj );
+% $m->callback( CallbackName => 'AfterBasics', QueueObj => $QueueObj, ARGSRef => \%ARGS );
 </div>
 
 <div id="ticket-create-message">
 </div>
 
 <div id="ticket-create-message">
@@ -304,41 +304,30 @@ if ($CloneTicket) {
     my $members = $CloneTicketObj->Members;
     my ( @members, @members_of, @refers, @refers_by, @depends, @depends_by );
     my $refers = $CloneTicketObj->RefersTo;
     my $members = $CloneTicketObj->Members;
     my ( @members, @members_of, @refers, @refers_by, @depends, @depends_by );
     my $refers = $CloneTicketObj->RefersTo;
+    my $get_link_value = sub {
+        my ($link, $type) = @_;
+        my $uri_method = $type . 'URI';
+        my $local_method = 'Local' . $type;
+        my $uri = $link->$uri_method;
+        return if $uri->IsLocal and
+                $uri->Object and
+                $uri->Object->isa('RT::Ticket') and
+                $uri->Object->Type eq 'reminder';
+
+        return $link->$local_method || $uri->URI;
+    };
     while ( my $refer = $refers->Next ) {
     while ( my $refer = $refers->Next ) {
-        push @refers, $refer->LocalTarget;
+        my $refer_value = $get_link_value->($refer, 'Target');
+        push @refers, $refer_value if defined $refer_value;
     }
     $clone->{'new-RefersTo'} = join ' ', @refers;
 
     my $refers_by = $CloneTicketObj->ReferredToBy;
     while ( my $refer_by = $refers_by->Next ) {
     }
     $clone->{'new-RefersTo'} = join ' ', @refers;
 
     my $refers_by = $CloneTicketObj->ReferredToBy;
     while ( my $refer_by = $refers_by->Next ) {
-        push @refers_by, $refer_by->LocalBase;
+        my $refer_by_value = $get_link_value->($refer_by, 'Base');
+        push @refers_by, $refer_by_value if defined $refer_by_value;
     }
     $clone->{'RefersTo-new'} = join ' ', @refers_by;
     }
     $clone->{'RefersTo-new'} = join ' ', @refers_by;
-    if (0) {    # Temporarily disabled
-        my $depends = $CloneTicketObj->DependsOn;
-        while ( my $depend = $depends->Next ) {
-            push @depends, $depend->LocalTarget;
-        }
-        $clone->{'new-DependsOn'} = join ' ', @depends;
-
-        my $depends_by = $CloneTicketObj->DependedOnBy;
-        while ( my $depend_by = $depends_by->Next ) {
-            push @depends_by, $depend_by->LocalBase;
-        }
-        $clone->{'DependsOn-new'} = join ' ', @depends_by;
-
-        while ( my $member = $members->Next ) {
-            push @members, $member->LocalBase;
-        }
-        $clone->{'MemberOf-new'} = join ' ', @members;
-
-        my $members_of = $CloneTicketObj->MemberOf;
-        while ( my $member_of = $members_of->Next ) {
-            push @members_of, $member_of->LocalTarget;
-        }
-        $clone->{'new-MemberOf'} = join ' ', @members_of;
-
-    }
 
     my $cfs = $CloneTicketObj->QueueObj->TicketCustomFields();
     while ( my $cf = $cfs->Next ) {
 
     my $cfs = $CloneTicketObj->QueueObj->TicketCustomFields();
     while ( my $cf = $cfs->Next ) {
index 6deb65c..0a29c97 100755 (executable)
@@ -55,7 +55,7 @@
 <& /Elements/ListActions, actions => \@Actions &>
 <& Elements/ShowUpdateStatus, Ticket => $TicketObj &>
 
 <& /Elements/ListActions, actions => \@Actions &>
 <& Elements/ShowUpdateStatus, Ticket => $TicketObj &>
 
-% $m->callback( %ARGS, Ticket => $TicketObj, CallbackName => 'BeforeShowSummary' );
+% $m->callback( %ARGS, Ticket => $TicketObj, Transactions => $transactions, Attachments => $attachments, CallbackName => 'BeforeShowSummary' );
 <div class="summary">
 <&| /Widgets/TitleBox, title => loc('Ticket metadata') &>
 <& /Ticket/Elements/ShowSummary,  Ticket => $TicketObj, Attachments => $attachments &>
 <div class="summary">
 <&| /Widgets/TitleBox, title => loc('Ticket metadata') &>
 <& /Ticket/Elements/ShowSummary,  Ticket => $TicketObj, Attachments => $attachments &>
@@ -63,7 +63,7 @@
 </div>
 <br />
 
 </div>
 <br />
 
-% $m->callback( Ticket => $TicketObj, %ARGS, CallbackName => 'BeforeShowHistory' );
+% $m->callback( Ticket => $TicketObj, %ARGS, Transactions => $transactions, Attachments => $attachments, CallbackName => 'BeforeShowHistory' );
 
 % if (not $ForceShowHistory and RT->Config->Get( 'DeferTransactionLoading', $session{'CurrentUser'} )) {
     <& /Ticket/Elements/ClickToShowHistory,
 
 % if (not $ForceShowHistory and RT->Config->Get( 'DeferTransactionLoading', $session{'CurrentUser'} )) {
     <& /Ticket/Elements/ClickToShowHistory,
@@ -83,6 +83,8 @@
 
 % $m->callback( %ARGS,
 %     Ticket       => $TicketObj,
 
 % $m->callback( %ARGS,
 %     Ticket       => $TicketObj,
+%     Transactions => $transactions,
+%     Attachments  => $attachments,
 %     CallbackName => 'AfterShowHistory',
 % );
 
 %     CallbackName => 'AfterShowHistory',
 % );
 
index 8393191..30c9a43 100644 (file)
@@ -83,7 +83,7 @@ $Toggle => 0
 </%ARGS>
 <span class="toggle-bookmark-<% $id %>">
 % my $url = RT->Config->Get('WebPath') ."/Helpers/Toggle/TicketBookmark?id=". $id;
 </%ARGS>
 <span class="toggle-bookmark-<% $id %>">
 % my $url = RT->Config->Get('WebPath') ."/Helpers/Toggle/TicketBookmark?id=". $id;
-<a align="right" href="<% $url %>" onclick="jQuery('.toggle-bookmark-<% $id |n%>').load('<% $url |n %>'); return false;" >
+<a align="right" href="<% $url %>" onclick="jQuery('.toggle-bookmark-'+<% $id |n,j%>).load(<% $url |n,j %>); return false;" >
 % if ( $bookmarked ) {
 <img src="<% RT->Config->Get('WebPath') %>/NoAuth/images/star.gif" alt="<% loc('Remove Bookmark') %>" style="border-style: none" />
 % } else {
 % if ( $bookmarked ) {
 <img src="<% RT->Config->Get('WebPath') %>/NoAuth/images/star.gif" alt="<% loc('Remove Bookmark') %>" style="border-style: none" />
 % } else {
index 4461b9a..5a9a477 100644 (file)
@@ -47,7 +47,7 @@
 %# END BPS TAGGED BLOCK }}}
 <div id="deferred_ticket_history">
     <& /Widgets/TitleBoxStart, title => 'History' &>
 %# END BPS TAGGED BLOCK }}}
 <div id="deferred_ticket_history">
     <& /Widgets/TitleBoxStart, title => 'History' &>
-        <a href="<% $display %>" onclick="jQuery('#deferred_ticket_history').text('<% loc('Loading...') %>').load('<% $url |n %>'); return false;" ><% loc('Show ticket history') %></a>
+        <a href="<% $display %>" onclick="jQuery('#deferred_ticket_history').text(<% loc('Loading...') |n,j%>).load(<% $url |n,j %>); return false;" ><% loc('Show ticket history') %></a>
     <& /Widgets/TitleBoxEnd &>
 </div>
 <%ARGS>
     <& /Widgets/TitleBoxEnd &>
 </div>
 <%ARGS>
index be6b264..4b0b4c4 100644 (file)
@@ -47,4 +47,4 @@
 %# END BPS TAGGED BLOCK }}}
 <span
     class="message-stanza-folder closed"
 %# END BPS TAGGED BLOCK }}}
 <span
     class="message-stanza-folder closed"
-    onclick="fold_message_stanza(this, '<%loc('Show quoted text')%>', '<%loc('Hide quoted text')%>');"><%loc('Show quoted text')%></span><br />\
+    onclick="fold_message_stanza(this, <%loc('Show quoted text') |n,j%>, <%loc('Hide quoted text') |n,j%>);"><%loc('Show quoted text')%></span><br />\
index 563b0f0..36d0d8e 100644 (file)
@@ -54,13 +54,14 @@ $Edit => 0
 <%init>
 
 $Ticket = LoadTicket($id) if ($id);
 <%init>
 
 $Ticket = LoadTicket($id) if ($id);
+my $resolve_status = $Ticket->QueueObj->Lifecycle->ReminderStatusOnResolve;
 
 my $count_reminders = RT::Reminders->new($session{'CurrentUser'});
 $count_reminders->Ticket($Ticket->id);
 my $count_tickets = $count_reminders->Collection;
 if (!$ShowCompleted) {
     # XXX: don't break encapsulation if we can avoid it
 
 my $count_reminders = RT::Reminders->new($session{'CurrentUser'});
 $count_reminders->Ticket($Ticket->id);
 my $count_tickets = $count_reminders->Collection;
 if (!$ShowCompleted) {
     # XXX: don't break encapsulation if we can avoid it
-    $count_tickets->FromSQL('Type = "reminder" AND RefersTo = "'.$Ticket->id.'" AND Status != "resolved"');
+    $count_tickets->FromSQL(q{Type = "reminder" AND RefersTo = "} .  $Ticket->id . qq{" AND Status != "$resolve_status" });
 }
 my $has_reminders = $count_tickets->Count;
 
 }
 my $has_reminders = $count_tickets->Count;
 
@@ -85,7 +86,7 @@ my $reminder_collection = $count_reminders->Collection;
 % my $visible = 0;
 % while ( my $reminder = $reminder_collection->Next ) {
 % $i++;
 % my $visible = 0;
 % while ( my $reminder = $reminder_collection->Next ) {
 % $i++;
-% if ( $reminder->Status eq 'resolved' && !$ShowCompleted ) {
+% if ( $reminder->Status eq $resolve_status && !$ShowCompleted ) {
 <tr class="hidden"><td><input type="hidden" class="hidden" name="Complete-Reminder-<% $reminder->id %>" value="1" /></td></tr>
 % $i++;
 % } elsif ($Edit) {
 <tr class="hidden"><td><input type="hidden" class="hidden" name="Complete-Reminder-<% $reminder->id %>" value="1" /></td></tr>
 % $i++;
 % } elsif ($Edit) {
@@ -105,7 +106,7 @@ my $reminder_collection = $count_reminders->Collection;
 %# we must always include resolved reminders due to the browser
 %# checkbox-with-false-value issue
 % while ( my $reminder = $reminder_collection->Next ) {
 %# we must always include resolved reminders due to the browser
 %# checkbox-with-false-value issue
 % while ( my $reminder = $reminder_collection->Next ) {
-% if ( $reminder->Status eq 'resolved' && !$ShowCompleted ) {
+% if ( $reminder->Status eq $resolve_status && !$ShowCompleted ) {
 <input type="hidden" class="hidden" name="Complete-Reminder-<% $reminder->id %>" value="1" />
 % }
 % }
 <input type="hidden" class="hidden" name="Complete-Reminder-<% $reminder->id %>" value="1" />
 % }
 % }
@@ -139,7 +140,7 @@ $Ticket
 $Index
 </%args>
 <tr class="<% $Index%2 ? 'oddline' : 'evenline' %>">
 $Index
 </%args>
 <tr class="<% $Index%2 ? 'oddline' : 'evenline' %>">
-<td class="entry"><input type="checkbox" value="1" name="Complete-Reminder-<% $Reminder->id %>" <% $Reminder->Status eq 'resolved' ? 'checked="checked"' : '' |n %> /></td>
+<td class="entry"><input type="checkbox" value="1" name="Complete-Reminder-<% $Reminder->id %>" <% $Reminder->Status eq $Reminder->QueueObj->Lifecycle->ReminderStatusOnResolve ? 'checked="checked"' : '' |n %> /></td>
 <td class="label"><&|/l&>Subject</&>:</td>
 <td class="entry" colspan="3"><input type="text" size="50" name="Reminder-Subject-<% $Reminder->id %>" value="<% $Reminder->Subject %>" /></td>
 </tr>
 <td class="label"><&|/l&>Subject</&>:</td>
 <td class="entry" colspan="3"><input type="text" size="50" name="Reminder-Subject-<% $Reminder->id %>" value="<% $Reminder->Subject %>" /></td>
 </tr>
@@ -160,7 +161,7 @@ $Index
 % my $dueobj = $Reminder->DueObj;
 % my $overdue = $dueobj->Unix > 0 && $dueobj->Diff < 0 ? 1 : 0;
 <tr class="<% $Index%2 ? 'oddline' : 'evenline' %>">
 % my $dueobj = $Reminder->DueObj;
 % my $overdue = $dueobj->Unix > 0 && $dueobj->Diff < 0 ? 1 : 0;
 <tr class="<% $Index%2 ? 'oddline' : 'evenline' %>">
-<td class="collection-as-table"><input type="checkbox" value="1" name="Complete-Reminder-<% $Reminder->id %>" <% $Reminder->Status eq 'resolved' ? 'checked="checked"' : '' |n %> /></td>
+<td class="collection-as-table"><input type="checkbox" value="1" name="Complete-Reminder-<% $Reminder->id %>" <% $Reminder->Status eq $Reminder->QueueObj->Lifecycle->ReminderStatusOnResolve ? 'checked="checked"' : '' |n %> /></td>
 <td class="collection-as-table"><% $Reminder->Subject %></td>
 <td class="collection-as-table"><% $overdue ? '<span class="overdue">' : '' |n %><% $dueobj->AgeAsString || loc('Not set') %><% $overdue ? '</span>' : '' |n %></td>
 <td class="collection-as-table"><& /Elements/ShowUser, User => $Reminder->OwnerObj &></td>
 <td class="collection-as-table"><% $Reminder->Subject %></td>
 <td class="collection-as-table"><% $overdue ? '<span class="overdue">' : '' |n %><% $dueobj->AgeAsString || loc('Not set') %><% $overdue ? '</span>' : '' |n %></td>
 <td class="collection-as-table"><& /Elements/ShowUser, User => $Reminder->OwnerObj &></td>
index b5e009c..909ea01 100755 (executable)
@@ -60,11 +60,12 @@ if ($ShowDisplayModes or $ShowTitle) {
     if ($ShowDisplayModes) {
         $titleright = '';
         
     if ($ShowDisplayModes) {
         $titleright = '';
         
-        my $open_all  = $m->interp->apply_escapes( loc("Show all quoted text"), 'h' );
-        my $close_all = $m->interp->apply_escapes( loc("Hide all quoted text"), 'h' );
+        my $open_all  = $m->interp->apply_escapes( loc("Show all quoted text"), 'j' );
+        my $open_html = $m->interp->apply_escapes( loc("Show all quoted text"), 'h' );
+        my $close_all = $m->interp->apply_escapes( loc("Hide all quoted text"), 'j' );
         $titleright .=    '<a href="#" data-direction="open" '
         $titleright .=    '<a href="#" data-direction="open" '
-                        . qq{onclick='return toggle_all_folds(this, "$open_all", "$close_all");'}
-                        . ">$open_all</a> &mdash; ";
+                        . qq{onclick="return toggle_all_folds(this, $open_all, $close_all);"}
+                        . ">$open_html</a> &mdash; ";
 
         if ($ShowHeaders) {
             $titleright .= qq{<a href="$URIFile?id=} .
 
         if ($ShowHeaders) {
             $titleright .= qq{<a href="$URIFile?id=} .
index 3e5f41f..8a8eef6 100755 (executable)
@@ -175,7 +175,9 @@ unless ( $DefaultTicketsTab eq 'None' ) {
 }
 
 my $TicketTemplate = "ShowRequestorTickets$DefaultTicketsTab";
 }
 
 my $TicketTemplate = "ShowRequestorTickets$DefaultTicketsTab";
-$TicketTemplate = "ShowRequestorTicketsActive" unless $m->comp_exists($TicketTemplate);
+$TicketTemplate = "ShowRequestorTicketsActive"
+    unless RT::Interface::Web->ComponentPathIsSafe($TicketTemplate)
+       and $m->comp_exists($TicketTemplate);
 </%INIT>
 <%ARGS>
 $Ticket=>undef
 </%INIT>
 <%ARGS>
 $Ticket=>undef
index 392ee86..d062156 100644 (file)
@@ -61,8 +61,7 @@
     class="onetime onetimecc"
     type="checkbox"
 % my $clean_addr = $txn_addresses{$addr}->format;
     class="onetime onetimecc"
     type="checkbox"
 % my $clean_addr = $txn_addresses{$addr}->format;
-% $clean_addr =~ s/'/\\'/g;
-    onClick="checkboxToInput('UpdateCc', 'UpdateCc-<%$addr%>','<%$clean_addr%>' );"
+    onClick="checkboxToInput('UpdateCc', <% "UpdateCc-$addr" |n,j%>, <%$clean_addr|n,j%> );"
     <% $ARGS{'UpdateCc-'.$addr} ? 'checked="checked"' : ''%> > <& /Elements/ShowUser, Address => $txn_addresses{$addr}&>
 %}
 </td></tr>
     <% $ARGS{'UpdateCc-'.$addr} ? 'checked="checked"' : ''%> > <& /Elements/ShowUser, Address => $txn_addresses{$addr}&>
 %}
 </td></tr>
@@ -77,8 +76,7 @@
     class="onetime onetimebcc"
     type="checkbox"
 % my $clean_addr = $txn_addresses{$addr}->format;
     class="onetime onetimebcc"
     type="checkbox"
 % my $clean_addr = $txn_addresses{$addr}->format;
-% $clean_addr =~ s/'/\\'/g;
-    onClick="checkboxToInput('UpdateBcc', 'UpdateBcc-<%$addr%>','<%$clean_addr%>' );"
+    onClick="checkboxToInput('UpdateBcc', <% "UpdateBcc-$addr" |n,j%>, <%$clean_addr|n,j%> );"
         <% $ARGS{'UpdateBcc-'.$addr} ? 'checked="checked"' : ''%>> 
 <& /Elements/ShowUser, Address => $txn_addresses{$addr}&>
 %}
         <% $ARGS{'UpdateBcc-'.$addr} ? 'checked="checked"' : ''%>> 
 <& /Elements/ShowUser, Address => $txn_addresses{$addr}&>
 %}
index 6269907..d15ce72 100644 (file)
@@ -51,7 +51,7 @@
 % $m->callback( CallbackName => 'BeforeActionList', %ARGS, Actions => \@results, ARGSRef => \%ARGS );
 <& /Elements/ListActions, actions => \@results &>
 <form method="post" action="<% RT->Config->Get('WebPath') . $m->request_comp->path %>?id=<% $id %>">
 % $m->callback( CallbackName => 'BeforeActionList', %ARGS, Actions => \@results, ARGSRef => \%ARGS );
 <& /Elements/ListActions, actions => \@results &>
 <form method="post" action="<% RT->Config->Get('WebPath') . $m->request_comp->path %>?id=<% $id %>">
-<a href="<% RT->Config->Get('WebURL') %>Ticket/Display.html?id=<% $txn->Ticket %>#txn-<% $id %>">
+<a href="<% RT->Config->Get('WebPath') %>/Ticket/Display.html?id=<% $txn->Ticket %>#txn-<% $id %>">
 <% loc('Return back to the ticket') %>
 </a>
 <& /Elements/Submit,
 <% loc('Return back to the ticket') %>
 </a>
 <& /Elements/Submit,
index e68aaed..c5479e3 100644 (file)
@@ -151,7 +151,7 @@ my $class = '';
 $class = 'class="hidden"' if $Level != 1 && !@Default;
 </%INIT>
 <% loc('Show Tickets Properties on [_1] level', $Level) %>
 $class = 'class="hidden"' if $Level != 1 && !@Default;
 </%INIT>
 <% loc('Show Tickets Properties on [_1] level', $Level) %>
-(<small><a href="#" onclick="hideshow('<% $id %>'); return false;"><% loc('open/close') %></a></small>):
+(<small><a href="#" onclick="hideshow(<% $id |n,j%>); return false;"><% loc('open/close') %></a></small>):
 <table id="<% $id %>" <% $class |n %>>
 % while ( my ($group, $list) = (splice @Available, 0, 2) ) {
 <tr><td><% loc($group) %>:</td><td>
 <table id="<% $id %>" <% $class |n %>>
 % while ( my ($group, $list) = (splice @Available, 0, 2) ) {
 <tr><td><% loc($group) %>:</td><td>
index 967a5e4..2163b81 100644 (file)
@@ -66,6 +66,7 @@ $ARGS{'id'} = $id = $ticket->id;
 require RT::Graph::Tickets;
 my $graph = RT::Graph::Tickets->TicketLinks(
     %ARGS,
 require RT::Graph::Tickets;
 my $graph = RT::Graph::Tickets->TicketLinks(
     %ARGS,
+    Graph  => undef,
     Ticket => $ticket,
 );
 </%INIT>
     Ticket => $ticket,
 );
 </%INIT>
index ba41445..89b1f37 100644 (file)
@@ -65,6 +65,7 @@ unless ( $ticket->id ) {
 require RT::Graph::Tickets;
 my $graph = RT::Graph::Tickets->TicketLinks(
     %ARGS,
 require RT::Graph::Tickets;
 my $graph = RT::Graph::Tickets->TicketLinks(
     %ARGS,
+    Graph  => undef,
     Ticket => $ticket,
 );
 
     Ticket => $ticket,
 );
 
index 28942e0..9dceb2a 100755 (executable)
@@ -51,7 +51,7 @@
 % $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Ticket => $Ticket);
 <& /Elements/ListActions, actions => \@results &>
 
 % $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Ticket => $Ticket);
 <& /Elements/ListActions, actions => \@results &>
 
-<form action="ModifyLinks.html" method="post">
+<form action="ModifyLinks.html" name="ModifyLinks" method="post">
 <input type="hidden" class="hidden" name="id" value="<%$Ticket->id%>" />
 % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS );
 % my (@extra);
 <input type="hidden" class="hidden" name="id" value="<%$Ticket->id%>" />
 % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS );
 % my (@extra);
index 238087f..69ac079 100644 (file)
@@ -56,7 +56,7 @@ my $z_index = 9999;
 
 <div id="<% $Name %>_Container" class="combobox <%$Class%>" style="z-index: <%$z_index--%>">
 <input name="<% $Name %>" id="<% $Name %>" class="combo-text" value="<% $Default || '' %>" type="text" <% $Size ? "size='$Size'" : '' |n %> autocomplete="off" />
 
 <div id="<% $Name %>_Container" class="combobox <%$Class%>" style="z-index: <%$z_index--%>">
 <input name="<% $Name %>" id="<% $Name %>" class="combo-text" value="<% $Default || '' %>" type="text" <% $Size ? "size='$Size'" : '' |n %> autocomplete="off" />
-<br style="display: none" /><span id="<% $Name %>_Button" class="combo-button">&#9660;</span><select name="List-<% $Name %>" id="<% $Name %>_List" class="combo-list" onchange="ComboBox_SimpleAttach(this, this.form['<% $Name %>']); " size="<% $Rows %>">
+<br style="display: none" /><span id="<% $Name %>_Button" class="combo-button">&#9660;</span><select name="List-<% $Name %>" id="<% $Name %>_List" class="combo-list" onchange="ComboBox_SimpleAttach(this, this.form[<% $Name |n,j%>]); " size="<% $Rows %>">
 <option style="display: none" value="">-</option>
 % foreach my $value (@Values) {
         <option value="<%$value%>"><% $value%></option>
 <option style="display: none" value="">-</option>
 % foreach my $value (@Values) {
         <option value="<%$value%>"><% $value%></option>
@@ -64,7 +64,7 @@ my $z_index = 9999;
 </select>
 </div>
 <script language="javascript"><!--
 </select>
 </div>
 <script language="javascript"><!--
-ComboBox_InitWith('<% $Name %>');
+ComboBox_InitWith(<% $Name |n,j %>);
 //--></script>
 </nobr>
 <%ARGS>
 //--></script>
 </nobr>
 <%ARGS>
index a031477..cbcc5c3 100755 (executable)
@@ -48,7 +48,7 @@
 <div class="titlebox<% $class ? " $class " : '' %><% $rolledup ? " rolled-up" : ""%>" id="<% $id %>">
   <div class="titlebox-title<% $title_class ? " $title_class" : ''%>">
 % if ($hideable) {
 <div class="titlebox<% $class ? " $class " : '' %><% $rolledup ? " rolled-up" : ""%>" id="<% $id %>">
   <div class="titlebox-title<% $title_class ? " $title_class" : ''%>">
 % if ($hideable) {
-    <span class="widget"><a href="#" onclick="return rollup('<%$tid%>');" title="Toggle visibility"></a></span>
+    <span class="widget"><a href="#" onclick="return rollup(<%$tid|n,j%>);" title="Toggle visibility"></a></span>
 % }
     <span class="left"><%
             $title_href ? qq[<a href="$title_href">] : '' | n
 % }
     <span class="left"><%
             $title_href ? qq[<a href="$title_href">] : '' | n
index c24b6cd..d6e0b79 100755 (executable)
@@ -126,7 +126,7 @@ if ( $ARGS{'QuickCreate'} ) {
 
 
 if ( $ARGS{'q'} ) {
 
 
 if ( $ARGS{'q'} ) {
-    RT::Interface::Web::Redirect(RT->Config->Get('WebURL')."Search/Simple.html?q=".$m->interp->apply_escapes($ARGS{q}));
+    RT::Interface::Web::Redirect(RT->Config->Get('WebURL')."Search/Simple.html?q=".$m->interp->apply_escapes($ARGS{q}, 'u'));
 }
 
 </%init>
 }
 
 </%init>
index 6396bc6..9f1b343 100755 (executable)
@@ -47,6 +47,6 @@
 %# END BPS TAGGED BLOCK }}}
 <%init>
  my $hand = ($session{'CurrentUser'} ||= RT::CurrentUser->new)->LanguageHandle;
 %# END BPS TAGGED BLOCK }}}
 <%init>
  my $hand = ($session{'CurrentUser'} ||= RT::CurrentUser->new)->LanguageHandle;
- $m->print($hand->maketext($m->content,@_));
+ $m->print($hand->maketext($m->content,map { $m->interp->apply_escapes($_, 'h') } @_));
  return(1);
 </%init>
  return(1);
 </%init>
index f3e0837..e0c023c 100644 (file)
@@ -1,11 +1 @@
 <& /elements/footer.html &>
 <& /elements/footer.html &>
-% if ( 0 ) {
-  <div id="bpscredits">
-    <& /Elements/Logo, ShowName => 0 &>
-    <div id="copyright">
-<&|/l,     '', '', '2010', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>', &>[_1] RT [_2] Copyright 1996-[_3] [_4].</&>
-</div>
-</div>
-</body>
-</html>
-% }
index 7c23194..052a1a5 100644 (file)
@@ -6,6 +6,7 @@ $CloneTicket => undef
 $m->callback( CallbackName => "Init", ARGSRef => \%ARGS );
 my $Queue = $ARGS{Queue};
 
 $m->callback( CallbackName => "Init", ARGSRef => \%ARGS );
 my $Queue = $ARGS{Queue};
 
+my $escape = sub { $m->interp->apply_escapes(shift, 'h') };
 
 my $showrows = sub {
     my @pairs = @_;
 
 my $showrows = sub {
     my @pairs = @_;
@@ -45,41 +46,30 @@ if ($CloneTicket) {
     my $members = $CloneTicketObj->Members;
     my ( @members, @members_of, @refers, @refers_by, @depends, @depends_by );
     my $refers = $CloneTicketObj->RefersTo;
     my $members = $CloneTicketObj->Members;
     my ( @members, @members_of, @refers, @refers_by, @depends, @depends_by );
     my $refers = $CloneTicketObj->RefersTo;
+    my $get_link_value = sub {
+        my ($link, $type) = @_;
+        my $uri_method = $type . 'URI';
+        my $local_method = 'Local' . $type;
+        my $uri = $link->$uri_method;
+        return if $uri->IsLocal and
+                $uri->Object and
+                $uri->Object->isa('RT::Ticket') and
+                $uri->Object->Type eq 'reminder';
+
+        return $link->$local_method || $uri->URI;
+    };
     while ( my $refer = $refers->Next ) {
     while ( my $refer = $refers->Next ) {
-        push @refers, $refer->LocalTarget;
+        my $refer_value = $get_link_value->($refer, 'Target');
+        push @refers, $refer_value if defined $refer_value;
     }
     $clone->{'new-RefersTo'} = join ' ', @refers;
 
     my $refers_by = $CloneTicketObj->ReferredToBy;
     while ( my $refer_by = $refers_by->Next ) {
     }
     $clone->{'new-RefersTo'} = join ' ', @refers;
 
     my $refers_by = $CloneTicketObj->ReferredToBy;
     while ( my $refer_by = $refers_by->Next ) {
-        push @refers_by, $refer_by->LocalBase;
+        my $refer_by_value = $get_link_value->($refer_by, 'Base');
+        push @refers_by, $refer_by_value if defined $refer_by_value;
     }
     $clone->{'RefersTo-new'} = join ' ', @refers_by;
     }
     $clone->{'RefersTo-new'} = join ' ', @refers_by;
-    if (0) {    # Temporarily disabled
-        my $depends = $CloneTicketObj->DependsOn;
-        while ( my $depend = $depends->Next ) {
-            push @depends, $depend->LocalTarget;
-        }
-        $clone->{'new-DependsOn'} = join ' ', @depends;
-
-        my $depends_by = $CloneTicketObj->DependedOnBy;
-        while ( my $depend_by = $depends_by->Next ) {
-            push @depends_by, $depend_by->LocalBase;
-        }
-        $clone->{'DependsOn-new'} = join ' ', @depends_by;
-
-        while ( my $member = $members->Next ) {
-            push @members, $member->LocalBase;
-        }
-        $clone->{'MemberOf-new'} = join ' ', @members;
-
-        my $members_of = $CloneTicketObj->MemberOf;
-        while ( my $member_of = $members_of->Next ) {
-            push @members_of, $member_of->LocalTarget;
-        }
-        $clone->{'new-MemberOf'} = join ' ', @members_of;
-
-    }
 
     my $cfs = $CloneTicketObj->QueueObj->TicketCustomFields();
     while ( my $cf = $cfs->Next ) {
 
     my $cfs = $CloneTicketObj->QueueObj->TicketCustomFields();
     while ( my $cf = $cfs->Next ) {
@@ -218,7 +208,7 @@ if ((!exists $ARGS{'AddMoreAttach'}) and (defined($ARGS{'id'}) and $ARGS{'id'} e
 
 <%perl>
 $showrows->(
 
 <%perl>
 $showrows->(
-    loc("Subject") => '<input name="Subject" size="30" maxsize="200" value="'.($ARGS{Subject} || '').'" />');
+    loc("Subject") => '<input name="Subject" size="30" maxsize="200" value="'.$escape->($ARGS{Subject} || '').'" />');
 </%perl>
     <span class="content-label label"><%loc("Describe the issue below")%></span>
         <& /Elements/MessageBox, exists $ARGS{Content}  ? (Default => $ARGS{Content}, IncludeSignature => 0 ) : ( QuoteTransaction => $QuoteTransaction ), Height => 5  &>
 </%perl>
     <span class="content-label label"><%loc("Describe the issue below")%></span>
         <& /Elements/MessageBox, exists $ARGS{Content}  ? (Default => $ARGS{Content}, IncludeSignature => 0 ) : ( QuoteTransaction => $QuoteTransaction ), Height => 5  &>
@@ -382,12 +372,12 @@ $showrows->(
 
 <%perl>
 $showrows->(
 
 <%perl>
 $showrows->(
-    loc("Depends on")     => '<input size="10" name="new-DependsOn" value="' . ($ARGS{'new-DependsOn'} || '' ). '" />',
-    loc("Depended on by") => '<input size="10" name="DependsOn-new" value="' . ($ARGS{'DependsOn-new'} || '' ) . '" />',
-    loc("Parents")        => '<input size="10" name="new-MemberOf" value="' . ($ARGS{'new-MemberOf'} || '') . '" />',
-    loc("Children")       => '<input size="10" name="MemberOf-new" value="' . ($ARGS{'MemberOf-new'} || '') . '" />',
-    loc("Refers to")      => '<input size="10" name="new-RefersTo" value="' . ($ARGS{'new-RefersTo'} || '') . '" />',
-    loc("Referred to by") => '<input size="10" name="RefersTo-new" value="' . ($ARGS{'RefersTo-new'} || ''). '" />'
+    loc("Depends on")     => '<input type="text" size="10" name="new-DependsOn" value="' . $escape->($ARGS{'new-DependsOn'} || '' ). '" />',
+    loc("Depended on by") => '<input type="text" size="10" name="DependsOn-new" value="' . $escape->($ARGS{'DependsOn-new'} || '' ) . '" />',
+    loc("Parents")        => '<input type="text" size="10" name="new-MemberOf" value="' . $escape->($ARGS{'new-MemberOf'} || '') . '" />',
+    loc("Children")       => '<input type="text" size="10" name="MemberOf-new" value="' . $escape->($ARGS{'MemberOf-new'} || '') . '" />',
+    loc("Refers to")      => '<input type="text" size="10" name="new-RefersTo" value="' . $escape->($ARGS{'new-RefersTo'} || '') . '" />',
+    loc("Referred to by") => '<input type="text" size="10" name="RefersTo-new" value="' . $escape->($ARGS{'RefersTo-new'} || ''). '" />'
 );
 </%perl>
 
 );
 </%perl>
 
index e979da3..4190bd3 100644 (file)
@@ -145,18 +145,18 @@ my $print_value = sub {
     }
     $m->out('</a>') if defined $linked && length $linked;
 
     }
     $m->out('</a>') if defined $linked && length $linked;
 
-    # This section automatically populates a<div with the "IncludeContentForValue" for this custom
+    # This section automatically populates a div with the "IncludeContentForValue" for this custom
     # field if it's been defined
     if ( $cf->IncludeContentForValue ) {
        my $vid = $value->id;
        $m->out(   '<div class="object_cf_value_include" id="object_cf_value_'. $vid .'">' );
        $m->print( loc("See also:") );
     # field if it's been defined
     if ( $cf->IncludeContentForValue ) {
        my $vid = $value->id;
        $m->out(   '<div class="object_cf_value_include" id="object_cf_value_'. $vid .'">' );
        $m->print( loc("See also:") );
-       $m->out(   '<a href="'. $value->IncludeContentForValue .'">' );
-       $m->print( $value->IncludeContentForValue );
+       $m->out(   '<a href="'. $m->interp->apply_escapes($value->IncludeContentForValue, 'h') .'">' );
+       $m->out( $m->interp->apply_escapes($value->IncludeContentForValue, 'h') );
        $m->out(   qq{</a></div>\n} );
        $m->out(   qq{</a></div>\n} );
-       $m->out(   qq{<script><!--\nahah('} );
-       $m->print( $value->IncludeContentForValue );
-       $m->out(   qq{', 'object_cf_value_$vid');\n--></script>\n} );
+       $m->out(   qq{<script><!--\njQuery('#object_cf_value_$vid').load(} );
+       $m->out(   $m->interp->apply_escapes($value->IncludeContentForValue, 'j') );
+       $m->out(   qq{);\n--></script>\n} );
     }
 };
 
     }
 };
 
index 16864b4..e688ea8 100644 (file)
@@ -31,7 +31,7 @@ my $search;
                 if ( $custom->Description eq $name ) { $search = $custom; last }
             }
             unless ( $search && $search->id ) {
                 if ( $custom->Description eq $name ) { $search = $custom; last }
             }
             unless ( $search && $search->id ) {
-                $m->out("Predefined search $name not found");
+                $m->out(loc("Predefined search [_1] not found", $m->interp->apply_escapes($name, 'h')));
                 return;
             }
         }
                 return;
             }
         }
index 9756e51..6fcaa49 100644 (file)
@@ -4,7 +4,7 @@ use Test::MockTime qw(set_fixed_time restore_time);
 use DateTime;
 
 use warnings; use strict;
 use DateTime;
 
 use warnings; use strict;
-use RT::Test tests => 172;
+use RT::Test tests => 173;
 use RT::User;
 use Test::Warn;
 
 use RT::User;
 use Test::Warn;
 
@@ -85,9 +85,11 @@ my $current_user;
     my $date = RT::Date->new(RT->SystemUser);
     is($date->Unix, 0, "new date returns 0 in Unix format");
     is($date->Get, '1970-01-01 00:00:00', "default is ISO format");
     my $date = RT::Date->new(RT->SystemUser);
     is($date->Unix, 0, "new date returns 0 in Unix format");
     is($date->Get, '1970-01-01 00:00:00', "default is ISO format");
-    is($date->Get(Format =>'SomeBadFormat'),
-       '1970-01-01 00:00:00',
-       "don't know format, return ISO format");
+    warning_like {
+        is($date->Get(Format =>'SomeBadFormat'),
+           '1970-01-01 00:00:00',
+           "don't know format, return ISO format");
+    } qr/Invalid date formatter/;
     is($date->Get(Format =>'W3CDTF'),
        '1970-01-01T00:00:00Z',
        "W3CDTF format with defaults");
     is($date->Get(Format =>'W3CDTF'),
        '1970-01-01T00:00:00Z',
        "W3CDTF format with defaults");
index cabb00e..50d08f7 100644 (file)
@@ -2,7 +2,7 @@
 use strict;
 use warnings;
 use RT;
 use strict;
 use warnings;
 use RT;
-use RT::Test tests => 16;
+use RT::Test tests => 18;
 
 
 {
 
 
 {
@@ -101,3 +101,16 @@ ok( $unlimittickets->Count > 0, "UnLimited tickets object should return tickets"
 
 }
 
 
 }
 
+
+{
+    my $tickets = RT::Tickets->new( RT->SystemUser );
+    $tickets->Limit( FIELD => 'id', OPERATOR => '>', VALUE => 0 );
+    my $count = $tickets->Count();
+    ok $count > 1, "found more than one ticket";
+    undef $count;
+
+    $tickets->Limit( FIELD => 'id', OPERATOR => '=', VALUE => 1, ENTRYAGGREGATOR => 'none' );
+    $count = $tickets->Count();
+    ok $count == 1, "found one ticket";
+}
+
index 40e2391..5825a10 100644 (file)
@@ -177,7 +177,7 @@ diag "deleted -> X via modify, only open is available";
     my @form_values = $input->possible_values;
     ok scalar @form_values, 'some options in the UI';
 
     my @form_values = $input->possible_values;
     ok scalar @form_values, 'some options in the UI';
 
-    is join('-', @form_values), '-open', 'only open and default available';
+    is join('-', @form_values), '-deleted-open', 'only default, current and open available';
 }
 
 diag "check illegal values and transitions";
 }
 
 diag "check illegal values and transitions";
index b02f979..7515e2c 100644 (file)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 use strict;
 use warnings;
 #!/usr/bin/perl
 use strict;
 use warnings;
-use RT::Test nodb => 1, tests => 8;
+use RT::Test nodb => 1, tests => 9;
 
 use_ok('RT::I18N');
 
 
 use_ok('RT::I18N');
 
@@ -59,11 +59,29 @@ diag q{newline and encoded file name};
 diag q{rfc2231};
 {
     my $str =
 diag q{rfc2231};
 {
     my $str =
-"filename*=ISO-8859-1''%74%E9%73%74%2E%74%78%74 filename*=ISO-8859-1''%74%E9%73%74%2E%74%78%74";
+"attachment; filename*=ISO-8859-1''%74%E9%73%74%2E%74%78%74";
     is(
     is(
-        RT::I18N::DecodeMIMEWordsToEncoding( $str, 'utf-8' ),
-        'filename=tést.txt filename=tést.txt',
-        'right decodig'
+        RT::I18N::DecodeMIMEWordsToEncoding( $str, 'utf-8', 'Content-Disposition' ),
+        'attachment; filename="tést.txt"',
+        'right decoding'
+    );
+}
+
+diag q{rfc2231 param continuations};
+{
+    # XXX TODO: test various forms of the continuation stuff
+    #       quotes around the values
+    my $hdr = <<'.';
+inline;
+ filename*0*=ISO-2022-JP'ja'%1b$B%3f7$7$$%25F%25%2d%259%25H%1b%28B;
+ filename*1*=%20;
+ filename*2*=%1b$B%25I%25%2d%25e%25a%25s%25H%1b%28B;
+ filename*3=.txt
+.
+    is(
+        RT::I18N::DecodeMIMEWordsToEncoding( $hdr, 'utf-8', 'Content-Disposition' ),
+        'inline; filename="新しいテキスト ドキュメント.txt"',
+        'decoded continuations as one string'
     );
 }
 
     );
 }
 
index 276b761..f984bf3 100644 (file)
@@ -75,7 +75,7 @@ my $cf;
 
 # test custom field values auto completer
 {
 
 # test custom field values auto completer
 {
-    $m->get_ok('/Helpers/Autocomplete/CustomFieldValues?term=eNo&Object---CustomField-'. $cf->id .'-Value');
+    $m->get_ok('/Helpers/Autocomplete/CustomFieldValues?term=eNo&Object---CustomField-'. $cf->id .'-Value&ContextId=1&ContextType=RT::Queue');
     require JSON;
     is_deeply(
         JSON::from_json( $m->content ),
     require JSON;
     is_deeply(
         JSON::from_json( $m->content ),
index 0abbfac..5a64d46 100644 (file)
@@ -5,7 +5,7 @@ use HTTP::Request::Common;
 use HTTP::Cookies;
 use LWP;
 use Encode;
 use HTTP::Cookies;
 use LWP;
 use Encode;
-use RT::Test tests => 56;
+use RT::Test tests => 70;
 
 my $cookie_jar = HTTP::Cookies->new;
 my ($baseurl, $agent) = RT::Test->started_ok;
 
 my $cookie_jar = HTTP::Cookies->new;
 my ($baseurl, $agent) = RT::Test->started_ok;
@@ -295,3 +295,34 @@ diag "click advanced, enter a valid SQL, but the field is lower cased";
     );
 }
 
     );
 }
 
+diag "make sure skipped order by field doesn't break search";
+{
+    my $t = RT::Test->create_ticket( Queue => 'General', Subject => 'test' );
+    ok $t && $t->id, 'created a ticket';
+
+    $agent->get_ok($url."Search/Edit.html");
+    ok($agent->form_name('BuildQueryAdvanced'), "found the form");
+    $agent->field("Query", "id = ". $t->id);
+    $agent->submit;
+
+    $agent->follow_link_ok({id => 'page-results'});
+    ok( $agent->find_link(
+        text      => $t->id,
+        url_regex => qr{/Ticket/Display\.html},
+    ), "link to the ticket" );
+
+    $agent->follow_link_ok({id => 'page-edit_search'});
+    $agent->form_name('BuildQuery');
+    $agent->field("OrderBy", 'Requestor.EmailAddress', 3);
+    $agent->submit;
+    $agent->form_name('BuildQuery');
+    is $agent->value('OrderBy', 1), 'id';
+    is $agent->value('OrderBy', 2), '';
+    is $agent->value('OrderBy', 3), 'Requestor.EmailAddress';
+
+    $agent->follow_link_ok({id => 'page-results'});
+    ok( $agent->find_link(
+        text      => $t->id,
+        url_regex => qr{/Ticket/Display\.html},
+    ), "link to the ticket" );
+}
index d429d30..835b24c 100644 (file)
@@ -196,16 +196,17 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) {
 
 # test REST login response
 {
 
 # test REST login response
 {
+    $agent = RT::Test::Web->new;
     my $requested = $url."REST/1.0/?user=root;pass=password";
     $agent->get($requested);
     is($agent->status, 200, "Loaded a page");
     is($agent->uri, $requested, "didn't redirect to /NoAuth/Login.html for REST");
     my $requested = $url."REST/1.0/?user=root;pass=password";
     $agent->get($requested);
     is($agent->status, 200, "Loaded a page");
     is($agent->uri, $requested, "didn't redirect to /NoAuth/Login.html for REST");
-    $agent->get_ok($url);
-    $agent->logout();
+    $agent->get_ok($url."REST/1.0");
 }
 
 # test REST login response for wrong pass
 {
 }
 
 # test REST login response for wrong pass
 {
+    $agent = RT::Test::Web->new;
     my $requested = $url."REST/1.0/?user=root;pass=passwrong";
     $agent->get_ok($requested);
     is($agent->status, 200, "Loaded a page");
     my $requested = $url."REST/1.0/?user=root;pass=passwrong";
     $agent->get_ok($requested);
     is($agent->status, 200, "Loaded a page");
@@ -217,6 +218,7 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) {
 
 # test REST login response for no creds
 {
 
 # test REST login response for no creds
 {
+    $agent = RT::Test::Web->new;
     my $requested = $url."REST/1.0/";
     $agent->get_ok($requested);
     is($agent->status, 200, "Loaded a page");
     my $requested = $url."REST/1.0/";
     $agent->get_ok($requested);
     is($agent->status, 200, "Loaded a page");
index 5e7194c..e38f201 100644 (file)
@@ -1,7 +1,9 @@
 #!/usr/bin/env perl
 use strict;
 use warnings;
 #!/usr/bin/env perl
 use strict;
 use warnings;
-use RT::Test tests => 18;
+use RT::Interface::REST;
+
+use RT::Test tests => 22;
 
 my ($baseurl, $m) = RT::Test->started_ok;
 
 
 my ($baseurl, $m) = RT::Test->started_ok;
 
@@ -69,3 +71,82 @@ for ("id: ticket/1",
         $m->content_contains($_);
 }
 
         $m->content_contains($_);
 }
 
+# Create ticket 2 for testing ticket links
+for (2 .. 3) {
+    $m->post("$baseurl/REST/1.0/ticket/edit", [
+        user    => 'root',
+        pass    => 'password',
+        content => $text,
+    ], Content_Type => 'form-data');
+
+    $m->post(
+        "$baseurl/REST/1.0/ticket/1/links",
+        [
+            user    => 'root',
+            pass    => 'password',
+        ],
+        Content_Type => 'form-data',
+    );
+
+    my $link_data = form_parse($m->content);
+
+    push @{$link_data->[0]->[1]}, 'DependsOn';
+    vpush($link_data->[0]->[2], 'DependsOn', $_);
+
+    $m->post(
+        "$baseurl/REST/1.0/ticket/1/links",
+        [
+            user    => 'root',
+            pass    => 'password',
+            content => form_compose($link_data),
+        ],
+        Content_Type => 'form-data',
+    );
+
+}
+
+# See what links get reported for ticket 1.
+$m->post(
+    "$baseurl/REST/1.0/ticket/1/links/show",
+    [
+        user    => 'root',
+        pass    => 'password',
+    ],
+    Content_Type => 'form-data',
+);
+
+# Verify that the link was added correctly.
+my $content = form_parse($m->content);
+my $depends_on = vsplit($content->[0]->[2]->{DependsOn});
+@$depends_on = sort @$depends_on;
+like(
+    $depends_on->[0], qr{/ticket/2$},
+    "Check ticket link.",
+) or diag("'content' obtained:\n", $m->content);
+
+like(
+    $depends_on->[1], qr{/ticket/3$},
+    "Check ticket link.",
+) or diag("'content' obtained:\n", $m->content);
+
+$m->post(
+    "$baseurl/REST/1.0/ticket/2/links/show",
+    [
+        user    => 'root',
+        pass    => 'password',
+    ],
+    Content_Type => 'form-data',
+);
+my ($link) = $m->content =~ m|DependedOnBy:.*ticket/(\d+)|;
+is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content);
+
+$m->post(
+    "$baseurl/REST/1.0/ticket/3/links/show",
+    [
+        user    => 'root',
+        pass    => 'password',
+    ],
+    Content_Type => 'form-data',
+);
+($link) = $m->content =~ m|DependedOnBy:.*ticket/(\d+)|;
+is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content);
index 6483a75..612c6e2 100644 (file)
@@ -30,13 +30,13 @@ use Test::LongString;
 
 {
     my $html = q[<span lang=EN-US style='font-family:"Century Gothic","sans-serif";'>oh hai I'm some text</span>];
 
 {
     my $html = q[<span lang=EN-US style='font-family:"Century Gothic","sans-serif";'>oh hai I'm some text</span>];
-    my $expected = q[<span style="font-family:&quot;Century Gothic&quot;,&quot;sans-serif&quot;;">oh hai I'm some text</span>];
+    my $expected = q[<span lang="EN-US" style="font-family:&quot;Century Gothic&quot;,&quot;sans-serif&quot;;">oh hai I'm some text</span>];
     is_string(scrub_html($html), $expected, "font lists");
 }
 
 {
     my $html = q[<span lang=EN-US style='font-size:7.5pt;font-family:"Century Gothic","sans-serif";color:#666666;mso-fareast-language:IT'>oh hai I'm some text</span>];
     is_string(scrub_html($html), $expected, "font lists");
 }
 
 {
     my $html = q[<span lang=EN-US style='font-size:7.5pt;font-family:"Century Gothic","sans-serif";color:#666666;mso-fareast-language:IT'>oh hai I'm some text</span>];
-    my $expected = q[<span style="font-size:7.5pt;font-family:&quot;Century Gothic&quot;,&quot;sans-serif&quot;;color:#666666;mso-fareast-language:IT">oh hai I'm some text</span>];
+    my $expected = q[<span lang="EN-US" style="font-size:7.5pt;font-family:&quot;Century Gothic&quot;,&quot;sans-serif&quot;;color:#666666;mso-fareast-language:IT">oh hai I'm some text</span>];
     is_string(scrub_html($html), $expected, "outlook html");
 }
 
     is_string(scrub_html($html), $expected, "outlook html");
 }
 
index be06ad9..1d74673 100644 (file)
@@ -227,6 +227,41 @@ diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_V
         like( $mail, qr/image\/png/,                       'att image content type' );
     }
 }
         like( $mail, qr/image\/png/,                       'att image content type' );
     }
 }
+RT::Test->clean_caught_mails;
+
+diag "Forward Ticket Template with a Subject: line" if $ENV{TEST_VERBOSE};
+{
+
+    require RT::Template;
+    my $template = RT::Template->new($RT::SystemUser);
+    $template->Load('Forward Ticket');
+
+    # prepend a Subject: line
+    $template->SetContent("Subject: OVERRIDING SUBJECT\n\n" . $template->Content);
+
+    my $ticket = RT::Test->create_ticket(
+        Subject => 'test ticket',
+        Queue   => 1,
+    );
+
+    $m->goto_ticket($ticket->Id);
+
+    $m->follow_link_ok(
+        { id => 'page-actions-forward' },
+        'follow 1st Forward to forward ticket'
+    );
+
+    $m->submit_form(
+        form_name => 'ForwardMessage',
+        fields    => {
+            To => 'rt-to@example.com',
+        },
+        button => 'ForwardAndReturn'
+    );
+
+    my ($mail) = RT::Test->fetch_caught_mails;
+    like($mail, qr/Subject: OVERRIDING SUBJECT/);
+}
 
 undef $m;
 done_testing;
 
 undef $m;
 done_testing;
index cb30e92..efb6151 100644 (file)
@@ -1,6 +1,6 @@
 use strict;
 use warnings;
 use strict;
 use warnings;
-use RT::Test tests => 106;
+use RT::Test tests => 146;
 
 my ( $baseurl, $m ) = RT::Test->started_ok;
 ok( $m->login, "Logged in" );
 
 my ( $baseurl, $m ) = RT::Test->started_ok;
 ok( $m->login, "Logged in" );
@@ -24,6 +24,16 @@ is( $deleted->Status, 'deleted', "deleted $deleted_id" );
 $inactive->SetStatus('resolved');
 is( $inactive->Status, 'resolved', 'resolved $inactive_id' );
 
 $inactive->SetStatus('resolved');
 is( $inactive->Status, 'resolved', 'resolved $inactive_id' );
 
+# Create an article for linking
+require RT::Class;
+my $class = RT::Class->new($RT::SystemUser);
+$class->Create(Name => 'test class');
+
+require RT::Article;
+my $article = RT::Article->new($RT::SystemUser);
+
+$article->Create(Class => $class->Id, Name => 'test article');
+
 for my $type ( "DependsOn", "MemberOf", "RefersTo" ) {
     for my $c (qw/base target/) {
         my $id;
 for my $type ( "DependsOn", "MemberOf", "RefersTo" ) {
     for my $c (qw/base target/) {
         my $id;
@@ -105,6 +115,59 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) {
         );
         $m->content_unlike( qr{$deleted_id.*?\[deleted\]}, "no deleted ticket",
         );
         );
         $m->content_unlike( qr{$deleted_id.*?\[deleted\]}, "no deleted ticket",
         );
+
+        diag "[$type]: Testing that reminders don't get copied for $c tickets";
+        {
+            my $ticket = RT::Test->create_ticket(
+                Subject => 'test ticket',
+                Queue   => 1,
+            );
+
+            $m->goto_ticket($ticket->Id);
+            $m->form_name('UpdateReminders');
+            $m->field('NewReminder-Subject' => 'hello test reminder subject');
+            $m->click_button(value => 'Save');
+            $m->text_contains('hello test reminder subject');
+
+            my $id = $ticket->Id;
+            my $type_value = my $link_field = $type;
+            if ($c eq 'base') {
+                $type_value = "new-$type_value";
+                $link_field    = "$link_field-$id";
+            }
+            else {
+                $type_value = "$type_value-new";
+                $link_field = "$id-$link_field";
+            }
+
+            if ($type eq 'RefersTo') {
+                $m->goto_ticket($ticket->Id);
+                $m->follow_link(id => 'page-links');
+
+                # add $baseurl as a link
+                $m->form_name('ModifyLinks');
+                $m->field($link_field => "$baseurl/test_ticket_reference");
+                $m->click('SubmitTicket');
+
+                # add an article as a link
+                $m->form_name('ModifyLinks');
+                $m->field($link_field => 'a:' . $article->Id);
+                $m->click('SubmitTicket');
+            }
+
+            my $depends_on_url = sprintf(
+                '%s/Ticket/Create.html?Queue=%s&CloneTicket=%s&%s=%s',
+                $baseurl, '1', $id, $type_value, $id,
+            );
+            $m->get_ok($depends_on_url);
+            $m->form_name('TicketCreate');
+            $m->click_button(value => 'Create');
+            $m->content_lacks('hello test reminder subject');
+            if ($type eq 'RefersTo') {
+                $m->text_contains("$baseurl/test_ticket_reference");
+                $m->text_contains("Article " . $article->Id . ': test article');
+            }
+        }
     }
 }
 
     }
 }