Dev -> 4.0.6. Clean upgrade from 4.0.5-5.
authorMikal Kolbein Gule <m.k.gule@usit.uio.no>
Mon, 18 Jun 2012 10:16:01 +0000 (12:16 +0200)
committerMikal Kolbein Gule <m.k.gule@usit.uio.no>
Mon, 18 Jun 2012 10:16:01 +0000 (12:16 +0200)
69 files changed:
bin/rt
bin/rt-crontool
bin/rt-mailgate
docs/UPGRADING-4.0
docs/hacking.pod
docs/security.pod
docs/web_deployment.pod
etc/RT_Config.pm
etc/RT_Config.pm.orig
etc/schema.mysql
lib/RT.pm
lib/RT/Action/SendEmail.pm
lib/RT/Config.pm
lib/RT/Dashboard/Mailer.pm
lib/RT/Generated.pm
lib/RT/Handle.pm
lib/RT/I18N.pm
lib/RT/Installer.pm
lib/RT/Interface/Email.pm
lib/RT/Interface/Web.pm
lib/RT/Interface/Web/Handler.pm
lib/RT/Interface/Web/QueryBuilder/Tree.pm
lib/RT/Lifecycle.pm
lib/RT/Links.pm
lib/RT/Reminders.pm
lib/RT/Report/Tickets.pm
lib/RT/Report/Tickets/Entry.pm
lib/RT/SearchBuilder.pm
lib/RT/Test.pm
lib/RT/Test/Web.pm
lib/RT/Tickets.pm
lib/RT/URI/fsck_com_article.pm
sbin/rt-fulltext-indexer
sbin/rt-server.fcgi
sbin/rt-shredder
sbin/rt-test-dependencies
sbin/standalone_httpd
share/html/Admin/Tools/Shredder/Elements/Object/RT--Attachment
share/html/Admin/Tools/Shredder/Elements/Object/RT--Ticket
share/html/Admin/Tools/Shredder/Elements/Object/RT--User
share/html/Admin/Users/Modify.html
share/html/Elements/CollectionList
share/html/Elements/Header
share/html/Elements/RT__Queue/ColumnMap
share/html/Elements/SelectOwner
share/html/Elements/SelectStatus
share/html/Elements/ShowLink
share/html/Elements/ShowSearch
share/html/Elements/Tabs
share/html/Install/Initialize.html
share/html/Install/index.html
share/html/NoAuth/Logout.html
share/html/Prefs/Search.html
share/html/REST/1.0/Forms/ticket/default
share/html/REST/1.0/Forms/ticket/links
share/html/REST/1.0/ticket/link
share/html/Search/Build.html
share/html/Search/Elements/BuildFormatString
share/html/Search/Elements/Chart
share/html/Search/Elements/PickBasics
share/html/Search/Elements/PickCFs
share/html/Search/Elements/PickCriteria
share/html/Search/Results.html
share/html/Ticket/Create.html
share/html/Ticket/Display.html
share/html/Ticket/Elements/Reminders
share/html/Ticket/GnuPG.html
share/html/Ticket/ModifyLinks.html
share/html/m/ticket/create

diff --git a/bin/rt b/bin/rt
index 683f8f8..0a1737f 100755 (executable)
--- a/bin/rt
+++ b/bin/rt
@@ -905,11 +905,6 @@ sub link {
     
     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;
@@ -1710,7 +1705,7 @@ sub prettyshow {
         }
         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};
     }
index b6a5b14..ee817d4 100755 (executable)
 use strict;
 use Carp;
 
-use lib '/www/data/rt/rt-perl/current-perl10/share/perl5';
-use lib '/www/data/rt/rt-perl/current-perl10/lib/perl5';
-use lib '/www/data/rt/rt-perl/current-perl10/lib64/perl5';
-
 # fix lib paths, some may be relative
 BEGIN {
     require File::Spec;
index 44a95cc..c07f6ab 100755 (executable)
@@ -252,20 +252,13 @@ sub check_failure {
     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;
-
-    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);
+    # XXX TODO 4.2: Remove the multi-line error strings in favor of something more concise
+    print STDERR <<"    ERROR";
+An Error Occurred
+=================
+
+@{[ $r->status_line ]}
+    ERROR
     print STDERR "\n$0: undefined server error\n" if $opts->{'debug'};
     return $self->tempfail();
 }
index a930134..4b64d2e 100644 (file)
@@ -106,3 +106,26 @@ with
   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
 
-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/>.
 
+
+=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
index 65065c5..4c3f73f 100644 (file)
@@ -67,6 +67,19 @@ spontaneously logged in as other users in the system.
 
 =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
index c34575a..15b7cb0 100644 (file)
@@ -1,7 +1,7 @@
 #
 # RT was configured with:
 #
-#   $ ./configure --prefix=/www/data/rt/4.0.5/ --with-web-user=httpd --with-web-group=httpd --with-rt-group=uio-rt --with-apachectl=/www/sbin/apachectl --with-db-type=Pg --with-db-dba=postgres --disable-gpg
+#   $ ./configure --prefix=/www/var/rt/ --with-web-user=httpd --with-web-group=httpd --with-rt-group=uio-rt --with-apachectl=/www/sbin/apachectl --with-db-type=Pg --with-db-dba=postgres --disable-gpg
 #
 
 package RT;
@@ -349,13 +349,8 @@ Set($StoreLoops, undef);
 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
 
-
 Set($MaxAttachmentSize, 10_000_000);
 
 =item C<$TruncateLongAttachments>
@@ -864,6 +859,8 @@ Set($CanonicalizeRedirectURLs, 0);
 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/
@@ -900,6 +897,8 @@ directory, or from http://www.crockford.com/javascript/jsmin.html
 
 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//);
@@ -1775,13 +1774,30 @@ Set($RestrictReferrer, 1);
 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 us aware that they are being logged in.  The default is off, for
+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
 
 
@@ -1851,10 +1867,9 @@ Set($AutoCreate, undef);
 
 =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
 
@@ -2191,6 +2206,14 @@ 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
@@ -2337,6 +2360,8 @@ Set(%Lifecycles,
             on_merge  => 'resolved',
             approved  => 'open',
             denied    => 'rejected',
+            reminder_on_open     => 'open',
+            reminder_on_resolve  => 'resolved',
         },
 
         transitions => {
@@ -2410,6 +2435,8 @@ Set(%Lifecycles,
         defaults => {
             on_create => 'new',
             on_merge => 'resolved',
+            reminder_on_open     => 'open',
+            reminder_on_resolve  => 'resolved',
         },
 
         transitions => {
@@ -2514,7 +2541,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'}
-        .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:#'}
index 90cf72c..c34575a 100644 (file)
@@ -1759,8 +1759,33 @@ This disables RT's clickjacking protection.
 
 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 us aware that they are being logged in.  The default is off, for
+backwards compatability.
+
+=cut
+
+Set($RestrictLoginReferrer, 0);
+
 =back
 
+
+
 =head1 Authorization and user configuration
 
 =over 4
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  ,
-  Content BLOB,
+  Content LONGBLOB,
   ContentType varchar(16) CHARACTER SET ascii,
   ObjectType varchar(64) CHARACTER SET ascii,
   ObjectId integer, # foreign key to anything
index dc2bb09..063f7f7 100644 (file)
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -415,6 +415,7 @@ sub InitClasses {
     require RT::Approval;
     require RT::Lifecycle;
     require RT::Link;
+    require RT::Links;
     require RT::Article;
     require RT::Articles;
     require RT::Class;
@@ -745,7 +746,7 @@ sub CanonicalizeGeneratedPaths {
 =head2 AddJavaScript
 
 helper method to add js files to C<JSFiles> config.
-to add extra css files, you can add the following line
+to add extra js files, you can add the following line
 in the plugin's main file:
 
     RT->AddJavaScript( 'foo.js', 'bar.js' ); 
index 94686b8..4ae1a8b 100644 (file)
@@ -348,7 +348,7 @@ sub AddAttachments {
 
     $MIMEObj->head->delete('RT-Attach-Message');
 
-    my $attachments = RT::Attachments->new( $self->TransactionObj->CreatorObj );
+    my $attachments = RT::Attachments->new( RT->SystemUser );
     $attachments->Limit(
         FIELD => 'TransactionId',
         VALUE => $self->TransactionObj->Id
index e17ad37..301b9f5 100644 (file)
@@ -610,6 +610,7 @@ our %META = (
             }
         }
     },
+    ReferrerWhitelist => { Type => 'ARRAY' },
     ResolveDefaultUpdateType => {
         PostLoadCheck => sub {
             my $self  = shift;
index 5062e4a..40b53b1 100644 (file)
@@ -252,7 +252,7 @@ SUMMARY
 
     $content = HTML::RewriteAttributes::Links->rewrite(
         $content,
-        RT->Config->Get('WebURL') . '/Dashboards/Render.html',
+        RT->Config->Get('WebURL') . 'Dashboards/Render.html',
     );
 
     $self->EmailDashboard(
index f448390..59b839e 100644 (file)
@@ -50,7 +50,7 @@ package RT;
 use warnings;
 use strict;
 
-our $VERSION = '4.0.5';
+our $VERSION = '4.0.6';
 
 
 
index 7f1e4f7..99d10e3 100644 (file)
@@ -746,6 +746,10 @@ sub InsertData {
     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,
@@ -1069,8 +1073,14 @@ sub InsertData {
         $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.");
 
index 971eaa1..cadf7cc 100644 (file)
@@ -219,13 +219,6 @@ sub SetMIMEEntityToEncoding {
 
     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);
@@ -292,7 +285,28 @@ sub DecodeMIMEWordsToEncoding {
     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
@@ -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;
index 3976ade..d12abb6 100644 (file)
@@ -252,7 +252,7 @@ sub CurrentValues {
 
 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 {
index 5d01d5d..385ba72 100644 (file)
@@ -748,16 +748,19 @@ sub SendForward {
     $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 )
index f04d0cc..c8b258f 100644 (file)
@@ -253,6 +253,7 @@ sub HandleRequest {
     ValidateWebConfig();
 
     DecodeARGS($ARGS);
+    local $HTML::Mason::Commands::DECODED_ARGS = $ARGS;
     PreprocessTimeUpdates($ARGS);
 
     InitializeMenu();
@@ -539,6 +540,10 @@ sub ShowRequestedPage {
 
     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 );
 
@@ -690,7 +695,6 @@ sub AttemptPasswordAuthentication {
 
         InstantiateNewSession();
         $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj;
-        SendSessionCookie();
 
         $m->callback( %$ARGS, CallbackName => 'SuccessfulLogin', CallbackPage => '/autohandler' );
 
@@ -745,6 +749,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;
+    SendSessionCookie();
 }
 
 sub SendSessionCookie {
@@ -1161,7 +1166,7 @@ sub ComponentRoots {
     return @roots;
 }
 
-my %is_whitelisted_path = (
+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.
@@ -1172,7 +1177,7 @@ sub IsCompCSRFWhitelisted {
     my $comp = shift;
     my $ARGS = shift;
 
-    return 1 if $is_whitelisted_path{$comp};
+    return 1 if $is_whitelisted_component{$comp};
 
     my %args = %{ $ARGS };
 
@@ -1200,6 +1205,11 @@ sub IsCompCSRFWhitelisted {
     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;
@@ -1209,11 +1219,16 @@ sub IsCompCSRFWhitelisted {
 
 sub IsRefererCSRFWhitelisted {
     my $referer = _NormalizeHost(shift);
-    my $config  = _NormalizeHost(RT->Config->Get('WebBaseURL'));
+    my $base_url = _NormalizeHost(RT->Config->Get('WebBaseURL'));
+    $base_url = $base_url->host_port;
 
-    return (1,$referer,$config) if $referer->host_port eq $config->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,$config);
+    return (0,$referer,$configs);
 }
 
 =head3 _NormalizeHost
@@ -1268,12 +1283,21 @@ EOT
             "your browser did not supply a Referrer header", # loc
         ) if !$ENV{HTTP_REFERER};
 
-    my ($whitelisted, $browser, $config) = IsRefererCSRFWhitelisted($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, $config->host_port);
+            $browser->host_port,
+            $configs->[0]);
 }
 
 sub ExpandCSRFToken {
@@ -1290,6 +1314,7 @@ sub ExpandCSRFToken {
     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
@@ -1304,21 +1329,9 @@ sub ExpandCSRFToken {
     return 1;
 }
 
-sub MaybeShowInterstitialCSRFPage {
+sub StoreRequestToken {
     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 = Digest::MD5::md5_hex(time . {} . $$ . rand(1024));
     my $user = $HTML::Mason::Commands::session{'CurrentUser'}->UserObj;
     my $data = {
@@ -1337,10 +1350,28 @@ sub MaybeShowInterstitialCSRFPage {
 
     $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 => $HTML::Mason::Commands::r->path_info,
+        OriginalURL => RT->Config->Get('WebPath') . $HTML::Mason::Commands::r->path_info,
         Reason => HTML::Mason::Commands::loc( $msg, @loc ),
         Token => $token,
     );
@@ -2348,10 +2379,11 @@ sub ProcessTicketReminders {
 
     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);
             }
-            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);
             }
 
@@ -3063,7 +3095,7 @@ following:
 
 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
+    H6 DIV UL OL LI DL DT DD PRE BLOCKQUOTE BDO
 );
 
 our %SCRUBBER_ALLOWED_ATTRIBUTES = (
@@ -3091,6 +3123,8 @@ our %SCRUBBER_ALLOWED_ATTRIBUTES = (
             )\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 = ();
index f96f66e..a740167 100644 (file)
@@ -69,7 +69,7 @@ sub DefaultHandlerArgs  { (
     ],
     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'), 
index c1bf132..2cfc889 100644 (file)
@@ -92,8 +92,8 @@ sub TraversePrePost {
 
 =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
 
@@ -110,10 +110,12 @@ sub GetReferencedQueues {
             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;
         }
     );
 
index edb1795..056599e 100644 (file)
@@ -367,6 +367,28 @@ sub DefaultOnMerge {
     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
index 0d8ed2f..ccc72d7 100644 (file)
@@ -91,15 +91,17 @@ sub Limit  {
 
     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') ) {
-       $self->OrderBy (ALIAS => 'main',
-                       FIELD => 'Target',
-                       ORDER => 'ASC');
+       $self->OrderByCols(
+            { ALIAS => 'main', FIELD => 'LocalTarget', ORDER => 'ASC' },
+            { ALIAS => 'main', FIELD => 'Target', ORDER => 'ASC' },
+        );
     }
     
 
index e7c28a8..2b66325 100644 (file)
@@ -137,7 +137,8 @@ sub Open {
     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',
@@ -149,7 +150,8 @@ sub Open {
 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',
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);
-            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 ) {
index f3eeb4d..87754c4 100644 (file)
@@ -83,6 +83,10 @@ sub LabelValue {
     return $value;
 }
 
+sub ObjectType {
+    return 'RT::Ticket';
+}
+
 RT::Base->_ImportOverlays();
 
 1;
index f6946e3..5ee7ecb 100644 (file)
@@ -105,10 +105,14 @@ sub JoinTransactions {
         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',
-        VALUE    => ref $self->NewItem,
+        VALUE    => $object_type,
     );
     $self->{'_sql_aliases'}{'transactions'} = $alias
         unless $args{'New'};
index 0d6da1b..7d69dd6 100644 (file)
@@ -409,7 +409,11 @@ sub bootstrap_db {
         $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();
@@ -556,6 +560,13 @@ sub __drop_database {
         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;
 }
@@ -1276,8 +1287,10 @@ sub started_ok {
 
     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 = @_;
 
-    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;
-    $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;
@@ -1314,6 +1347,10 @@ sub test_app {
             }
         );
     }
+
+    close $warn_fh;
+    $stashwarnings->add_warning( $warnings ) if $warnings;
+
     return $app;
 }
 
@@ -1346,7 +1383,8 @@ sub start_plack_server {
         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);
     }
 
index 28ca3b8..c2d9ac3 100644 (file)
@@ -52,15 +52,19 @@ use strict;
 use warnings;
 
 use base qw(Test::WWW::Mechanize);
+use Scalar::Util qw(weaken);
 
-require RT::Test;
+BEGIN { require RT::Test; }
 require Test::More;
 
+my $instance;
+
 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;
@@ -100,6 +104,7 @@ sub login {
         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;
@@ -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;
index 8a84a7b..a5fa74e 100644 (file)
@@ -3395,7 +3395,11 @@ sub _RestrictionsToClauses {
         # 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;
 }
index 503434a..0c09b7c 100644 (file)
@@ -188,7 +188,7 @@ Otherwise, return its URI
 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);
index 829302f..a2aae5e 100755 (executable)
@@ -127,7 +127,7 @@ unless ( $fts_config->{'Enable'} ) {
     print STDERR <<EOT;
 
 Full text search is disabled in your RT configuration.  Run
-/www/data/rt/4.0.5/sbin/rt-setup-fulltext-index to configure and enable it.
+/www/var/rt/sbin/rt-setup-fulltext-index to configure and enable it.
 
 EOT
     exit 1;
index a4ce649..7dc100d 100755 (executable)
 use warnings;
 use strict;
 
-use lib '/www/data/rt/rt-perl/current-perl10/share/perl5';
-use lib '/www/data/rt/rt-perl/current-perl10/lib/perl5';
-use lib '/www/data/rt/rt-perl/current-perl10/lib64/perl5';
-
 # fix lib paths, some may be relative
 BEGIN {
     die <<EOT if ${^TAINT};
@@ -95,6 +91,7 @@ if (grep { m/help/ } @ARGV) {
 
 require RT;
 RT->LoadConfig();
+RT->InitLogging();
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
index 38d37c7..058726c 100755 (executable)
@@ -74,7 +74,7 @@ should wipeout.
 
 =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
@@ -110,10 +110,6 @@ L<RT::Shredder>
 use strict;
 use warnings FATAL => 'all';
 
-use lib '/www/data/rt/rt-perl/current-perl10/share/perl5';
-use lib '/www/data/rt/rt-perl/current-perl10/lib/perl5';
-use lib '/www/data/rt/rt-perl/current-perl10/lib64/perl5';
-
 # fix lib paths, some may be relative
 BEGIN {
     require File::Spec;
index 76f6949..fd754aa 100755 (executable)
@@ -189,6 +189,8 @@ File::ShareDir
 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
@@ -245,8 +247,6 @@ CGI::Emulate::PSGI
 .
 
 $deps{'MAILGATE'} = [ text_to_hash( << '.') ];
-HTML::TreeBuilder
-HTML::FormatText
 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
-Plack::Middleware::Test::StashWarnings
+Plack::Middleware::Test::StashWarnings 0.06
 Test::LongString
 .
 
@@ -339,6 +339,7 @@ Net::SMTP
 $deps{'DASHBOARDS'} = [ text_to_hash( << '.') ];
 HTML::RewriteAttributes 0.04
 MIME::Types
+URI 1.59
 .
 
 $deps{'GRAPHVIZ'} = [ text_to_hash( << '.') ];
index 757f012..7dc100d 100755 (executable)
@@ -91,6 +91,7 @@ if (grep { m/help/ } @ARGV) {
 
 require RT;
 RT->LoadConfig();
+RT->InitLogging();
 require Module::Refresh if RT->Config->Get('DevelMode');
 
 require RT::Handle;
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)");
-<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>
index 13547ad..35f1aa8 100644 (file)
@@ -48,6 +48,6 @@
 <%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>
index f77169a..d7627eb 100644 (file)
@@ -48,6 +48,6 @@
 <%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>
index 9be2fb7..ab4a98a 100644 (file)
 % 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>
 % }
@@ -276,12 +276,6 @@ else {
 
        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);
index 22da66e..2d1817f 100644 (file)
@@ -68,7 +68,7 @@ if ( $Rows ) {
 # 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(
index 4b0c2b8..4a6ac26 100644 (file)
@@ -57,7 +57,7 @@
     <& /Elements/Framekiller &>
 
 % if ($Refresh && $Refresh =~ /^(\d+)/ && $1 > 0) {
-%   my $URL = $m->notes->{LogoutURL}; $URL = $URL ? ";URL=$URL" : "";
+%   my $URL = $m->notes->{RefreshURL}; $URL = $URL ? ";URL=$URL" : "";
     <meta http-equiv="refresh" content="<% "$1$URL" %>" />
 % }
 
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') },
     },
+    Lifecycle => {
+        title => 'Lifecycle',
+        attribute => 'Lifecycle',
+        value => sub { return $_[0]->Lifecycle->Name },
+    },
 };
 
 foreach my $field (qw(
     Name Description CorrespondAddress CommentAddress
     InitialPriority FinalPriority DefaultDueIn
-    Lifecycle
 )) {
     $COLUMN_MAP->{$field} = {
         title => $field,
index cc64e24..37a5971 100644 (file)
@@ -59,8 +59,12 @@ if ($TicketObj) {
     @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.
@@ -77,5 +81,5 @@ $m->callback(
 <%ARGS>
 $TicketObj  => undef
 $QueueObj   => undef
-$cfqueues   => undef
+%Queues     => ()
 </%ARGS>
index 137e799..33a2edc 100644 (file)
@@ -65,6 +65,8 @@ if ( @Statuses ) {
 }
 elsif ( $TicketObj ) {
     my $current = $TicketObj->Status;
+    push @status, $current;
+
     my $lifecycle = $TicketObj->QueueObj->Lifecycle;
 
     my %has = ();
@@ -77,8 +79,15 @@ elsif ( $TicketObj ) {
 }
 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>
@@ -88,6 +97,7 @@ $Name => undef
 @Statuses => ()
 $TicketObj => undef
 $QueueObj => undef
+%Queues => ()
 
 $Default => ''
 $SkipDeleted => 0
index 8913a32..1727fa3 100644 (file)
@@ -45,7 +45,7 @@
 %# 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'));
 <%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 3dd001f..4b96bbf 100644 (file)
@@ -79,7 +79,7 @@ if ($SavedSearch) {
         # 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',
index 3e66bfb..95cc21a 100644 (file)
@@ -245,7 +245,7 @@ my $build_admin_menu = sub {
         my $section;
         if ( $request_path =~ m|^/Admin/$type/?(?:index.html)?$|
              || (    $request_path =~ m|^/Admin/$type/(?:Modify.html)$|
-                  && $m->request_args->{'Create'} )
+                  && $DECODED_ARGS->{'Create'} )
            )
         {
             $section = $tabs;
@@ -260,11 +260,11 @@ my $build_admin_menu = sub {
     }
 
     if ( $request_path =~ m{^/Admin/Queues} ) {
-        if ( $m->request_args->{'id'} && $m->request_args->{'id'} =~ /^\d+$/
+        if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/
                 ||
-              $m->request_args->{'Queue'} && $m->request_args->{'Queue'} =~ /^\d+$/
+              $DECODED_ARGS->{'Queue'} && $DECODED_ARGS->{'Queue'} =~ /^\d+$/
                 ) {
-            my $id = $m->request_args->{'Queue'} || $m->request_args->{'id'};
+            my $id = $DECODED_ARGS->{'Queue'} || $DECODED_ARGS->{'id'};
             my $queue_obj = RT::Queue->new( $session{'CurrentUser'} );
             $queue_obj->Load($id);
 
@@ -294,8 +294,8 @@ my $build_admin_menu = sub {
         }
     }
     if ( $request_path =~ m{^/Admin/Users} ) {
-        if ( $m->request_args->{'id'} && $m->request_args->{'id'} =~ /^\d+$/ ) {
-            my $id = $m->request_args->{'id'};
+        if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
+            my $id = $DECODED_ARGS->{'id'};
             my $obj = RT::User->new( $session{'CurrentUser'} );
             $obj->Load($id);
 
@@ -312,8 +312,8 @@ my $build_admin_menu = sub {
     }
 
     if ( $request_path =~ m{^/Admin/Groups} ) {
-        if ( $m->request_args->{'id'} && $m->request_args->{'id'} =~ /^\d+$/ ) {
-            my $id = $m->request_args->{'id'};
+        if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
+            my $id = $DECODED_ARGS->{'id'};
             my $obj = RT::Group->new( $session{'CurrentUser'} );
             $obj->Load($id);
 
@@ -327,8 +327,8 @@ my $build_admin_menu = sub {
     }
 
     if ( $request_path =~ m{^/Admin/CustomFields/} ) {
-        if ( $m->request_args->{'id'} && $m->request_args->{'id'} =~ /^\d+$/ ) {
-            my $id = $m->request_args->{'id'};
+        if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
+            my $id = $DECODED_ARGS->{'id'};
             my $obj = RT::CustomField->new( $session{'CurrentUser'} );
             $obj->Load($id);
 
@@ -353,7 +353,7 @@ my $build_admin_menu = sub {
 
     if ( $request_path =~ m{^/Admin/Articles/Classes/} ) {
         my $tabs = PageMenu();
-        if ( my $id = $m->request_args->{'id'} ) {
+        if ( my $id = $DECODED_ARGS->{'id'} ) {
             my $obj = RT::CustomField->new( $session{'CurrentUser'} );
             $obj->Load($id);
 
@@ -490,7 +490,7 @@ my $build_main_nav = sub {
         $about_me->child( logout => title => loc('Logout'), path => '/NoAuth/Logout.html' );
     }
     if ( $request_path =~ m{^/Dashboards/(\d+)?}) {
-        if ( my $id = ( $1 || $m->request_args->{'id'} ) ) {
+        if ( my $id = ( $1 || $DECODED_ARGS->{'id'} ) ) {
             my $obj = RT::Dashboard->new( $session{'CurrentUser'} );
             $obj->LoadById($id);
             if ( $obj and $obj->id ) {
@@ -506,7 +506,7 @@ my $build_main_nav = sub {
 
 
     if ( $request_path =~ m{^/Ticket/} ) {
-        if ( ( $m->request_args->{'id'} || '' ) =~ /^(\d+)$/ ) {
+        if ( ( $DECODED_ARGS->{'id'} || '' ) =~ /^(\d+)$/ ) {
             my $id  = $1;
             my $obj = RT::Ticket->new( $session{'CurrentUser'} );
             $obj->Load($id);
@@ -654,17 +654,17 @@ my $build_main_nav = sub {
             && $request_path !~ m{^/Search/Simple\.html}
         )
         || (   $request_path =~ m{^/Search/Simple\.html}
-            && $m->request_args->{'q'} )
+            && $DECODED_ARGS->{'q'} )
       )
     {
         my $search = Menu()->child('search');
         my $args      = '';
         my $has_query = '';
         my $current_search = $session{"CurrentSearchHash"} || {};
-        my $search_id = $m->request_args->{'SavedSearchLoad'} || $m->request_args->{'SavedSearchId'} || $current_search->{'SearchId'} || '';
-        my $chart_id = $m->request_args->{'SavedChartSearchId'} || $current_search->{SavedChartSearchId};
+        my $search_id = $DECODED_ARGS->{'SavedSearchLoad'} || $DECODED_ARGS->{'SavedSearchId'} || $current_search->{'SearchId'} || '';
+        my $chart_id = $DECODED_ARGS->{'SavedChartSearchId'} || $current_search->{SavedChartSearchId};
 
-        $has_query = 1 if ( $m->request_args->{'Query'} or $current_search->{'Query'} );
+        $has_query = 1 if ( $DECODED_ARGS->{'Query'} or $current_search->{'Query'} );
 
         my %query_args;
         my %fallback_query_args = (
@@ -673,12 +673,12 @@ my $build_main_nav = sub {
             (
                 map {
                     my $p = $_;
-                    $p => $m->request_args->{$p} || $current_search->{$p}
+                    $p => $DECODED_ARGS->{$p} || $current_search->{$p}
                 } qw(Query Format OrderBy Order Page)
             ),
             RowsPerPage => (
-                defined $m->request_args->{'RowsPerPage'}
-                ? $m->request_args->{'RowsPerPage'}
+                defined $DECODED_ARGS->{'RowsPerPage'}
+                ? $DECODED_ARGS->{'RowsPerPage'}
                 : $current_search->{'RowsPerPage'}
             ),
         );
@@ -773,8 +773,8 @@ my $build_main_nav = sub {
     }
 
     if ( $request_path =~ m{^/Article/} ) {
-        if ( $m->request_args->{'id'} && $m->request_args->{'id'} =~ /^\d+$/ ) {
-            my $id = $m->request_args->{'id'};
+        if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
+            my $id = $DECODED_ARGS->{'id'};
             my $tabs = PageMenu();
 
             $tabs->child( display => title => loc('Display'), path => "/Articles/Article/Display.html?id=".$id );
@@ -788,7 +788,7 @@ my $build_main_nav = sub {
         my $tabs = PageMenu();
         $tabs->child( search => title => loc("Search"),       path => "/Articles/Article/Search.html" );
         $tabs->child( create => title => loc("New Article" ), path => "/Articles/Article/PreCreate.html" );
-        if ( $request_path =~ m{^/Articles/Article/} and ( $m->request_args->{'id'} || '' ) =~ /^(\d+)$/ ) {
+        if ( $request_path =~ m{^/Articles/Article/} and ( $DECODED_ARGS->{'id'} || '' ) =~ /^(\d+)$/ ) {
             my $id  = $1;
             my $obj = RT::Article->new( $session{'CurrentUser'} );
             $obj->Load($id);
@@ -821,7 +821,7 @@ my $build_selfservice_nav = sub {
     $queues->UnLimit;
 
     my $queue_count = 0;
-    my $queue_id    = 1;
+    my $queue_id;
 
     while ( my $queue = $queues->Next ) {
         next unless $queue->CurrentUserHasRight('CreateTicket');
@@ -831,14 +831,14 @@ my $build_selfservice_nav = sub {
     }
 
 
+    if ( $queue_count > 1 ) {
+        Menu->child( new => title => loc('New ticket'), path => '/SelfService/CreateTicketInQueue.html' );
+    } elsif ( $queue_id ) {
+        Menu->child( new => title => loc('New ticket'), path => '/SelfService/Create.html?Queue=' . $queue_id );
+    }
     my $tickets = Menu->child( tickets => title => loc('Tickets'));
     $tickets->child( open   => title => loc('Open tickets'),   path => '/SelfService/' );
     $tickets->child( closed => title => loc('Closed tickets'), path => '/SelfService/Closed.html' );
-    if ( $queue_count > 1 ) {
-        $tickets->child( new => title => loc('New ticket'),    path => '/SelfService/CreateTicketInQueue.html' );
-    } else {
-        $tickets->child( new => title => loc('New ticket'),    path => '/SelfService/Create.html?Queue=' . $queue_id );
-    }
 
 
     my $username = '<span class="current-user">'
index 47d7616..0cc39af 100644 (file)
@@ -125,7 +125,7 @@ if ( $Run ) {
                 $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);
index 61fb89e..78069af 100644 (file)
@@ -92,7 +92,7 @@
 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
index b8e119a..20024cc 100644 (file)
@@ -81,5 +81,5 @@ if (keys %session) {
 }
 
 $m->callback( %ARGS, CallbackName => 'AfterSessionDelete' );
-$m->notes->{LogoutURL} = $URL;
+$m->notes->{RefreshURL} = $URL;
 </%INIT>
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',
-    cfqueues => {}, %ARGS
+    %ARGS
 );
 
 if ($ARGS{'Save'}) {
index 9ae803d..9a2212b 100644 (file)
@@ -149,10 +149,16 @@ else {
             }
             # 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;
                 }
@@ -348,9 +354,15 @@ else {
         }
         # Set custom field
         elsif ($key =~ /^$cf_spec/) {
-            my $cf = RT::CustomField->new( RT->SystemUser );
             $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.";
             }
index e2e1830..bf4f257 100644 (file)
@@ -100,7 +100,8 @@ if ($changes) {
                     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;
index aa80b0d..8d3345f 100644 (file)
@@ -81,10 +81,9 @@ if ($id && $object && $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 .= "Invalid ticket id: '$bad'.\n";
+    $output .= "Invalid ticket id: '$id'.\n";
     $status = "400 Bad Request";
     goto OUTPUT;
 }
index 802fe05..c670559 100644 (file)
@@ -78,7 +78,7 @@
 
 
 <div id="pick-criteria">
-    <& Elements/PickCriteria, query => $query{'Query'}, cfqueues => $queues &>
+    <& Elements/PickCriteria, query => $query{'Query'}, queues => $queues &>
 </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'&>
@@ -275,7 +275,7 @@ my ( $AvailableColumns, $CurrentFormat );
 ( $query{'Format'}, $AvailableColumns, $CurrentFormat ) = $m->comp(
     'Elements/BuildFormatString',
     %ARGS,
-    cfqueues => $queues,
+    queues => $queues,
     Format => $query{'Format'},
 );
 
@@ -308,7 +308,7 @@ if ( $ARGS{'DoSearch'} ) {
         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;
 }
 
index e8ebd6e..5ee09cf 100644 (file)
@@ -48,7 +48,7 @@
 <%ARGS>
 $Format => RT->Config->Get('DefaultSearchResultFormat')
 
-%cfqueues => ()
+%queues => ()
 
 $Face => undef
 $Size => undef
@@ -102,17 +102,11 @@ my @fields = qw(
 $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);
-    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;
 
index 01b78c7..be05da3 100644 (file)
@@ -130,10 +130,10 @@ my ($i,$total);
                          );
 </%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">
-<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>
index 7371067..ce31b96 100644 (file)
@@ -103,7 +103,7 @@ my @lines = (
         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',
-            Arguments => { ValueAttribute => 'Name' },
+            Arguments => { ValueAttribute => 'Name', Queues => \%queues },
         },
     },
     {
@@ -212,3 +212,6 @@ my @lines = (
 $m->callback( Conditions => \@lines );
 
 </%INIT>
+<%ARGS>
+%queues => ()
+</%ARGS>
index 4b9a88b..f2dc21f 100644 (file)
 % }
 <%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);
-    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(
@@ -124,10 +114,10 @@ while ( my $CustomField = $CustomFields->Next ) {
     push @lines, \%line;
 }
 
-$m->callback( Conditions => \@lines, Queues => \%cfqueues );
+$m->callback( Conditions => \@lines, Queues => \%queues );
 
 </%INIT>
 
 <%ARGS>
-%cfqueues => undef
+%queues => ()
 </%ARGS>
index 8524ae7..5db2c06 100644 (file)
@@ -51,8 +51,8 @@
 
 
 
-<& PickBasics &>
-<& PickCFs, cfqueues => \%cfqueues &>
+<& PickBasics, queues => \%queues &>
+<& PickCFs, queues => \%queues &>
 
 <tr class="separator"><td colspan="3"><hr /></td></tr>
 <tr>
@@ -68,5 +68,5 @@
 <%ARGS>
 $addquery => 0
 $query => undef
-%cfqueues => undef
+%queues => ()
 </%ARGS>
index 0040d2a..171b38d 100644 (file)
@@ -46,7 +46,7 @@
 %#
 %# 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, 
@@ -148,6 +148,16 @@ if ($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(
index 8d31efa..fcd34b1 100644 (file)
       <& /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">
@@ -299,41 +299,30 @@ if ($CloneTicket) {
     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 ) {
-        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 ) {
-        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;
-    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 ) {
index e2d501e..5e84a50 100644 (file)
@@ -55,7 +55,7 @@
 <& /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 &>
@@ -63,7 +63,7 @@
 </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,
@@ -83,6 +83,8 @@
 
 % $m->callback( %ARGS,
 %     Ticket       => $TicketObj,
+%     Transactions => $transactions,
+%     Attachments  => $attachments,
 %     CallbackName => 'AfterShowHistory',
 % );
 
index 563b0f0..36d0d8e 100644 (file)
@@ -54,13 +54,14 @@ $Edit => 0
 <%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
-    $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;
 
@@ -85,7 +86,7 @@ my $reminder_collection = $count_reminders->Collection;
 % 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) {
@@ -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 ) {
-% 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" />
 % }
 % }
@@ -139,7 +140,7 @@ $Ticket
 $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>
@@ -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' %>">
-<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>
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 %>">
-<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,
index 28942e0..9dceb2a 100644 (file)
@@ -51,7 +51,7 @@
 % $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);
index eb20230..b42787d 100644 (file)
@@ -93,41 +93,30 @@ if ($CloneTicket) {
     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 ) {
-        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 ) {
-        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;
-    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 ) {