--- /dev/null
+package FS::API;
+
+use FS::Conf;
+use FS::Record qw( qsearch qsearchs );
+use FS::cust_main;
+use FS::cust_location;
+use FS::cust_pay;
+use FS::cust_credit;
+use FS::cust_refund;
+
+=head1 NAME
+
+FS::API - Freeside backend API
+
+=head1 SYNOPSIS
+
+ use FS::API;
+
+=head1 DESCRIPTION
+
+This module implements a backend API for advanced back-office integration.
+
+In contrast to the self-service API, which authenticates an end-user and offers
+functionality to that end user, the backend API performs a simple shared-secret
+authentication and offers full, administrator functionality, enabling
+integration with other back-office systems.
+
+If accessing this API remotely with XML-RPC or JSON-RPC, be careful to block
+the port by default, only allow access from back-office servers with the same
+security precations as the Freeside server, and encrypt the communication
+channel (for exampple, with an SSH tunnel or VPN) rather than accessing it
+in plaintext.
+
+=head1 METHODS
+
+=over 4
+
+=item insert_payment
+
+Example:
+
+ my $result = FS::API->insert_payment(
+ 'secret' => 'sharingiscaring',
+ 'custnum' => 181318,
+ 'payby' => 'CASH',
+ 'paid' => '54.32',
+
+ #optional
+ '_date' => 1397977200, #UNIX timestamp
+ );
+
+ if ( $result->{'error'} ) {
+ die $result->{'error'};
+ } else {
+ #payment was inserted
+ print "paynum ". $result->{'paynum'};
+ }
+
+=cut
+
+#enter cash payment
+sub insert_payment {
+ my($class, %opt) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ #less "raw" than this? we are the backoffice API, and aren't worried
+ # about version migration ala cust_main/cust_location here
+ my $cust_pay = new FS::cust_pay { %opt };
+ my $error = $cust_pay->insert( 'manual'=>1 );
+ return { 'error' => $error,
+ 'paynum' => $cust_pay->paynum,
+ };
+}
+
+# pass the phone number ( from svc_phone )
+sub insert_payment_phonenum {
+ my($class, %opt) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ $class->_by_phonenum('insert_payment', %opt);
+
+}
+
+sub _by_phonenum {
+ my($class, $method, %opt) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ my $phonenum = delete $opt{'phonenum'};
+
+ my $svc_phone = qsearchs('svc_phone', { 'phonenum' => $phonenum } )
+ or return { 'error' => 'Unknown phonenum' };
+
+ my $cust_pkg = $svc_phone->cust_svc->cust_pkg
+ or return { 'error' => 'Unlinked phonenum' };
+
+ $opt{'custnum'} = $cust_pkg->custnum;
+
+ $class->$method(%opt);
+
+}
+
+=item insert_credit
+
+Example:
+
+ my $result = FS::API->insert_credit(
+ 'secret' => 'sharingiscaring',
+ 'custnum' => 181318,
+ 'amount' => '54.32',
+
+ #optional
+ '_date' => 1397977200, #UNIX timestamp
+ );
+
+ if ( $result->{'error'} ) {
+ die $result->{'error'};
+ } else {
+ #credit was inserted
+ print "crednum ". $result->{'crednum'};
+ }
+
+=cut
+
+#Enter credit
+sub insert_credit {
+ my($class, %opt) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ $opt{'reasonnum'} ||= $conf->config('api_credit_reason');
+
+ #less "raw" than this? we are the backoffice API, and aren't worried
+ # about version migration ala cust_main/cust_location here
+ my $cust_credit = new FS::cust_credit { %opt };
+ my $error = $cust_credit->insert;
+ return { 'error' => $error,
+ 'crednum' => $cust_credit->crednum,
+ };
+}
+
+# pass the phone number ( from svc_phone )
+sub insert_credit_phonenum {
+ my($class, %opt) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ $class->_by_phonenum('insert_credit', %opt);
+
+}
+
+=item insert_refund
+
+Example:
+
+ my $result = FS::API->insert_refund(
+ 'secret' => 'sharingiscaring',
+ 'custnum' => 181318,
+ 'payby' => 'CASH',
+ 'refund' => '54.32',
+
+ #optional
+ '_date' => 1397977200, #UNIX timestamp
+ );
+
+ if ( $result->{'error'} ) {
+ die $result->{'error'};
+ } else {
+ #refund was inserted
+ print "refundnum ". $result->{'crednum'};
+ }
+
+=cut
+
+#Enter cash refund.
+sub insert_refund {
+ my($class, %opt) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ # when github pull request #24 is merged,
+ # will have to change over to default reasonnum like credit
+ # but until then, this will do
+ $opt{'reason'} ||= 'API refund';
+
+ #less "raw" than this? we are the backoffice API, and aren't worried
+ # about version migration ala cust_main/cust_location here
+ my $cust_refund = new FS::cust_refund { %opt };
+ my $error = $cust_refund->insert;
+ return { 'error' => $error,
+ 'refundnum' => $cust_refund->refundnum,
+ };
+}
+
+# pass the phone number ( from svc_phone )
+sub insert_refund_phonenum {
+ my($class, %opt) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ $class->_by_phonenum('insert_refund', %opt);
+
+}
+
+#---
+
+# "2 way syncing" ? start with non-sync pulling info here, then if necessary
+# figure out how to trigger something when those things change
+
+# long-term: package changes?
+
+=item new_customer
+
+=cut
+
+#certainly false laziness w/ClientAPI::Signup new_customer/new_customer_minimal
+# but approaching this from a clean start / back-office perspective
+# i.e. no package/service, no immediate credit card run, etc.
+
+sub new_customer {
+ my( $class, %opt ) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ #default agentnum like signup_server-default_agentnum?
+
+ #same for refnum like signup_server-default_refnum
+
+ my $cust_main = new FS::cust_main ( {
+ 'agentnum' => $agentnum,
+ 'refnum' => $opt{refnum}
+ || $conf->config('signup_server-default_refnum'),
+ 'payby' => 'BILL',
+
+ map { $_ => $opt{$_} } qw(
+ agentnum refnum agent_custid referral_custnum
+ last first company
+ daytime night fax mobile
+ payby payinfo paydate paycvv payname
+ ),
+
+ } );
+
+ my @invoicing_list = $opt{'invoicing_list'}
+ ? split( /\s*\,\s*/, $opt{'invoicing_list'} )
+ : ();
+ push @invoicing_list, 'POST' if $opt{'postal_invoicing'};
+
+ my ($bill_hash, $ship_hash);
+ foreach my $f (FS::cust_main->location_fields) {
+ # avoid having to change this in front-end code
+ $bill_hash->{$f} = $opt{"bill_$f"} || $opt{$f};
+ $ship_hash->{$f} = $opt{"ship_$f"};
+ }
+
+ my $bill_location = FS::cust_location->new($bill_hash);
+ my $ship_location;
+ # we don't have an equivalent of the "same" checkbox in selfservice^Wthis API
+ # so is there a ship address, and if so, is it different from the billing
+ # address?
+ if ( length($ship_hash->{address1}) > 0 and
+ grep { $bill_hash->{$_} ne $ship_hash->{$_} } keys(%$ship_hash)
+ ) {
+
+ $ship_location = FS::cust_location->new( $ship_hash );
+
+ } else {
+ $ship_location = $bill_location;
+ }
+
+ $cust_main->set('bill_location' => $bill_location);
+ $cust_main->set('ship_location' => $ship_location);
+
+ $error = $cust_main->insert( {}, \@invoicing_list );
+ return { 'error' => $error } if $error;
+
+ return { 'error' => '',
+ 'custnum' => $cust_main->custnum,
+ };
+
+}
+
+=item customer_info
+
+=cut
+
+#some false laziness w/ClientAPI::Myaccount customer_info/customer_info_short
+
+use vars qw( @cust_main_editable_fields @location_editable_fields );
+@cust_main_editable_fields = qw(
+ first last company daytime night fax mobile
+);
+# locale
+# payby payinfo payname paystart_month paystart_year payissue payip
+# ss paytype paystate stateid stateid_state
+@location_editable_fields = qw(
+ address1 address2 city county state zip country
+);
+
+sub customer_info {
+ my( $class, %opt ) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $opt{custnum} })
+ or return { 'error' => 'Unknown custnum' };
+
+ my %return = (
+ 'error' => '',
+ 'display_custnum' => $cust_main->display_custnum,
+ 'name' => $cust_main->first. ' '. $cust_main->get('last'),
+ 'balance' => $cust_main->balance,
+ 'status' => $cust_main->status,
+ 'statuscolor' => $cust_main->statuscolor,
+ );
+
+ $return{$_} = $cust_main->get($_)
+ foreach @cust_main_editable_fields;
+
+ for (@location_editable_fields) {
+ $return{$_} = $cust_main->bill_location->get($_)
+ if $cust_main->bill_locationnum;
+ $return{'ship_'.$_} = $cust_main->ship_location->get($_)
+ if $cust_main->ship_locationnum;
+ }
+
+ my @invoicing_list = $cust_main->invoicing_list;
+ $return{'invoicing_list'} =
+ join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list );
+ $return{'postal_invoicing'} =
+ 0 < ( grep { $_ eq 'POST' } @invoicing_list );
+
+ #generally, the more useful data from the cust_main record the better.
+ # well, tell me what you want
+
+ return \%return;
+
+}
+
+#I also monitor for changes to the additional locations that are applied to
+# packages, and would like for those to be exportable as well. basically the
+# location data passed with the custnum.
+sub location_info {
+ my( $class, %opt ) = @_;
+ my $conf = new FS::Conf;
+ return { 'error' => 'Incorrect shared secret' }
+ unless $opt{secret} eq $conf->config('api_shared_secret');
+
+ my @cust_location = qsearch('cust_location', { 'custnum' => $opt{custnum} });
+
+ my %return = (
+ 'error' => '',
+ 'locations' => [ map $_->hashref, @cust_location ],
+ );
+
+ return \%return;
+}
+
+#Advertising sources?
+
+=back
+
+1;
1, ##nobalance
);
-warn $return{first} = $cust_main->first;
-warn $return{'last'} = $cust_main->get('last');
+ $return{first} = $cust_main->first;
+ $return{'last'} = $cust_main->get('last');
$return{name} = $cust_main->first. ' '. $cust_main->get('last');
$return{payby} = $cust_main->payby;
|| $conf->config('signup_server-default_refnum'),
( map { $_ => $template_cust->$_ } qw(
- last first company daytime night fax
+ last first company daytime night fax mobile
)
),
map { $_ => $packet->{$_} } qw(
last first ss company
- daytime night fax stateid stateid_state
+ daytime night fax mobile
+ stateid stateid_state
payby
payinfo paycvv paydate payname paystate paytype
paystart_month paystart_year payissue
map { $_ => $packet->{$_} } qw(
last first ss company
- daytime night fax
+ daytime night fax mobile
),
} );
use strict;
use vars qw($DEBUG $AUTOLOAD);
-use XMLRPC::Lite; # for XMLRPC::Data
+use FS::XMLRPC_Lite; #XMLRPC::Lite, for XMLRPC::Data
use FS::ClientAPI;
$DEBUG = 0;
};
}
-
-#XXX submit patch to SOAP::Lite
-
-use XMLRPC::Transport::HTTP;
-
-package XMLRPC::Transport::HTTP::Server;
-
-@XMLRPC::Transport::HTTP::Server::ISA = qw(SOAP::Transport::HTTP::Server);
-
-sub initialize; *initialize = \&XMLRPC::Server::initialize;
-sub make_fault; *make_fault = \&XMLRPC::Transport::HTTP::CGI::make_fault;
-sub make_response; *make_response = \&XMLRPC::Transport::HTTP::CGI::make_response;
-
1;
'section' => 'self-service',
'description' => 'Suspend reason when customers suspend their own packages. Set to nothing to disallow self-suspension.',
'type' => 'select-sub',
+ #false laziness w/api_credit_reason
'options_sub' => sub { require FS::Record;
require FS::reason;
my $type = qsearchs('reason_type',
'type' => 'text',
},
+ {
+ 'key' => 'api_shared_secret',
+ 'section' => 'API',
+ 'description' => 'Shared secret for back-office API authentication',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'xmlrpc_api',
+ 'section' => 'API',
+ 'description' => 'Enable the back-office API XML-RPC server (on port 8008).',
+ 'type' => 'checkbox',
+ },
+
+# {
+# 'key' => 'jsonrpc_api',
+# 'section' => 'API',
+# 'description' => 'Enable the back-office API JSON-RPC server (on port 8081).',
+# 'type' => 'checkbox',
+# },
+
+ {
+ 'key' => 'api_credit_reason',
+ 'section' => 'API',
+ 'description' => 'Default reason for back-office API credits',
+ 'type' => 'select-sub',
+ #false laziness w/api_credit_reason
+ 'options_sub' => sub { require FS::Record;
+ require FS::reason;
+ my $type = qsearchs('reason_type',
+ { class => 'R' })
+ or return ();
+ map { $_->reasonnum => $_->reason }
+ FS::Record::qsearch('reason',
+ { reason_type => $type->typenum }
+ );
+ },
+ 'option_sub' => sub { require FS::Record;
+ require FS::reason;
+ my $reason = FS::Record::qsearchs(
+ 'reason', { 'reasonnum' => shift }
+ );
+ $reason ? $reason->reason : '';
+ },
+ },
+
{ key => "apacheroot", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
{ key => "apachemachine", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
{ key => "apachemachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
--- /dev/null
+package FS::Daemon::Preforking;
+use base 'Exporter';
+
+=head1 NAME
+
+FS::Daemon::Preforking - A preforking web server
+
+=head1 SYNOPSIS
+
+ use FS::Daemon::Preforking qw( freeside_init1 freeside_init2 daemon_run );
+
+ my $me = 'mydaemon'; #keep unique among fs daemons, for logfiles etc.
+
+ freeside_init1($me); #daemonize, drop root and connect to freeside
+
+ #do setup tasks which should throw an error to the shell starting the daemon
+
+ freeside_init2($me); #move logging to logfile and disassociate from terminal
+
+ #do setup tasks which will warn/error to the log file, such as declining to
+ # run if our config is not in place
+
+ daemon_run(
+ 'port' => 5454, #keep unique among fs daemons
+ 'handle_request' => \&handle_request,
+ );
+
+ sub handle_request {
+ my $request = shift; #HTTP::Request object
+
+ #... do your thing
+
+ return $response; #HTTP::Response object
+
+ }
+
+=head1 AUTHOR
+
+Based on L<http://www.perlmonks.org/?node_id=582781> by Justin Hawkins
+
+and L<http://poe.perl.org/?POE_Cookbook/Web_Server_With_Forking>
+
+=cut
+
+use warnings;
+use strict;
+
+use constant DEBUG => 0; # Enable much runtime information.
+use constant MAX_PROCESSES => 10; # Total server process count.
+#use constant TESTING_CHURN => 0; # Randomly test process respawning.
+
+use vars qw( @EXPORT_OK $FREESIDE_LOG $SERVER_PORT $user $handle_request );
+@EXPORT_OK = qw( freeside_init1 freeside_init2 daemon_run );
+$FREESIDE_LOG = '%%%FREESIDE_LOG%%%';
+
+use POE 1.2; # Base features.
+use POE::Filter::HTTPD; # For serving HTTP content.
+use POE::Wheel::ReadWrite; # For socket I/O.
+use POE::Wheel::SocketFactory; # For serving socket connections.
+
+use FS::Daemon qw( daemonize1 drop_root logfile daemonize2 );
+use FS::UID qw( adminsuidsetup forksuidsetup dbh );
+
+#use FS::TicketSystem;
+
+sub freeside_init1 {
+ my $name = shift;
+
+ $user = shift @ARGV or die &usage($name);
+
+ $FS::Daemon::NOSIG = 1;
+ $FS::Daemon::PID_NEWSTYLE = 1;
+ daemonize1($name);
+
+ POE::Kernel->has_forked(); #daemonize forks...
+
+ drop_root();
+
+ adminsuidsetup($user);
+}
+
+sub freeside_init2 {
+ my $name = shift;
+
+ logfile("$FREESIDE_LOG/$name.log");
+
+ daemonize2();
+
+}
+
+sub daemon_run {
+ my %opt = @_;
+ $SERVER_PORT = $opt{port};
+ $handle_request = $opt{handle_request};
+
+ #parent doesn't need to hold a DB connection open
+ dbh->disconnect;
+ undef $FS::UID::dbh;
+
+ server_spawn(MAX_PROCESSES);
+ POE::Kernel->run();
+ #exit;
+
+}
+
+### Spawn the main server. This will run as the parent process.
+
+sub server_spawn {
+ my ($max_processes) = @_;
+
+ POE::Session->create(
+ inline_states => {
+ _start => \&server_start,
+ _stop => \&server_stop,
+ do_fork => \&server_do_fork,
+ got_error => \&server_got_error,
+ got_sig_int => \&server_got_sig_int,
+ got_sig_child => \&server_got_sig_child,
+ got_connection => \&server_got_connection,
+ _child => sub { undef },
+ },
+ heap => { max_processes => MAX_PROCESSES },
+ );
+}
+
+### The main server session has started. Set up the server socket and
+### bookkeeping information, then fork the initial child processes.
+
+sub server_start {
+ my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
+
+ $heap->{server} = POE::Wheel::SocketFactory->new
+ ( BindPort => $SERVER_PORT,
+ SuccessEvent => "got_connection",
+ FailureEvent => "got_error",
+ Reuse => "yes",
+ );
+
+ $kernel->sig( INT => "got_sig_int" );
+ $kernel->sig( TERM => "got_sig_int" ); #huh
+
+ $heap->{children} = {};
+ $heap->{is_a_child} = 0;
+
+ warn "Server $$ has begun listening on port $SERVER_PORT\n";
+
+ $kernel->yield("do_fork");
+}
+
+### The server session has shut down. If this process has any
+### children, signal them to shutdown too.
+
+sub server_stop {
+ my $heap = $_[HEAP];
+ DEBUG and warn "Server $$ stopped.\n";
+
+ if ( my @children = keys %{ $heap->{children} } ) {
+ DEBUG and warn "Server $$ is signaling children to stop.\n";
+ kill INT => @children;
+ }
+}
+
+### The server session has encountered an error. Shut it down.
+
+sub server_got_error {
+ my ( $heap, $syscall, $errno, $error ) = @_[ HEAP, ARG0 .. ARG2 ];
+ warn( "Server $$ got $syscall error $errno: $error\n",
+ "Server $$ is shutting down.\n",
+ );
+ delete $heap->{server};
+}
+
+### The server has a need to fork off more children. Only honor that
+### request form the parent, otherwise we would surely "forkbomb".
+### Fork off as many child processes as we need.
+
+sub server_do_fork {
+ my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
+
+ return if $heap->{is_a_child};
+
+ #my $current_children = keys %{ $heap->{children} };
+ #for ( $current_children + 2 .. $heap->{max_processes} ) {
+ while (scalar(keys %{$heap->{children}}) < $heap->{max_processes}) {
+
+ DEBUG and warn "Server $$ is attempting to fork.\n";
+
+ my $pid = fork();
+
+ unless ( defined($pid) ) {
+ DEBUG and
+ warn( "Server $$ fork failed: $!\n",
+ "Server $$ will retry fork shortly.\n",
+ );
+ $kernel->delay( do_fork => 1 );
+ return;
+ }
+
+ # Parent. Add the child process to its list.
+ if ($pid) {
+ $heap->{children}->{$pid} = 1;
+ $kernel->sig_child($pid, "got_sig_child");
+ next;
+ }
+
+ # Child. Clear the child process list.
+ $kernel->has_forked();
+ DEBUG and warn "Server $$ forked successfully.\n";
+ $heap->{is_a_child} = 1;
+ $heap->{children} = {};
+
+ #freeside db connection, etc.
+ forksuidsetup($user);
+
+ #why isn't this needed ala freeside-selfservice-server??
+ #FS::TicketSystem->init();
+
+ return;
+ }
+}
+
+### The server session received SIGINT. Don't handle the signal,
+### which in turn will trigger the process to exit gracefully.
+
+sub server_got_sig_int {
+ my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
+ DEBUG and warn "Server $$ received SIGINT/TERM.\n";
+
+ if ( my @children = keys %{ $heap->{children} } ) {
+ DEBUG and warn "Server $$ is signaling children to stop.\n";
+ kill INT => @children;
+ }
+
+ delete $heap->{server};
+ $kernel->sig_handled();
+}
+
+### The server session received a SIGCHLD, indicating that some child
+### server has gone away. Remove the child's process ID from our
+### list, and trigger more fork() calls to spawn new children.
+
+sub server_got_sig_child {
+ my ( $kernel, $heap, $child_pid ) = @_[ KERNEL, HEAP, ARG1 ];
+
+ return unless delete $heap->{children}->{$child_pid};
+
+ DEBUG and warn "Server $$ reaped child $child_pid.\n";
+ $kernel->yield("do_fork") if exists $_[HEAP]->{server};
+}
+
+### The server session received a connection request. Spawn off a
+### client handler session to parse the request and respond to it.
+
+sub server_got_connection {
+ my ( $heap, $socket, $peer_addr, $peer_port ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
+
+ DEBUG and warn "Server $$ received a connection.\n";
+
+ POE::Session->create(
+ inline_states => {
+ _start => \&client_start,
+ _stop => \&client_stop,
+ got_request => \&client_got_request,
+ got_flush => \&client_flushed_request,
+ got_error => \&client_got_error,
+ _parent => sub { 0 },
+ },
+ heap => {
+ socket => $socket,
+ peer_addr => $peer_addr,
+ peer_port => $peer_port,
+ },
+ );
+
+# # Gracefully exit if testing process churn.
+# delete $heap->{server}
+# if TESTING_CHURN and $heap->{is_a_child} and ( rand() < 0.1 );
+}
+
+### The client handler has started. Wrap its socket in a ReadWrite
+### wheel to begin interacting with it.
+
+sub client_start {
+ my $heap = $_[HEAP];
+
+ $heap->{client} = POE::Wheel::ReadWrite->new
+ ( Handle => $heap->{socket},
+ Filter => POE::Filter::HTTPD->new(),
+ InputEvent => "got_request",
+ ErrorEvent => "got_error",
+ FlushedEvent => "got_flush",
+ );
+
+ DEBUG and warn "Client handler $$/", $_[SESSION]->ID, " started.\n";
+}
+
+### The client handler has stopped. Log that fact.
+
+sub client_stop {
+ DEBUG and warn "Client handler $$/", $_[SESSION]->ID, " stopped.\n";
+}
+
+### The client handler has received a request. If it's an
+### HTTP::Response object, it means some error has occurred while
+### parsing the request. Send that back and return immediately.
+### Otherwise parse and process the request, generating and sending an
+### HTTP::Response object in response.
+
+sub client_got_request {
+ my ( $heap, $request ) = @_[ HEAP, ARG0 ];
+
+ DEBUG and
+ warn "Client handler $$/", $_[SESSION]->ID, " is handling a request.\n";
+
+ if ( $request->isa("HTTP::Response") ) {
+ $heap->{client}->put($request);
+ return;
+ }
+
+ forksuidsetup($user) unless dbh && dbh->ping;
+
+ my $response = &{ $handle_request }( $request );
+
+ $heap->{client}->put($response);
+}
+
+### The client handler received an error. Stop the ReadWrite wheel,
+### which also closes the socket.
+
+sub client_got_error {
+ my ( $heap, $operation, $errnum, $errstr ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
+ DEBUG and
+ warn( "Client handler $$/", $_[SESSION]->ID,
+ " got $operation error $errnum: $errstr\n",
+ "Client handler $$/", $_[SESSION]->ID, " is shutting down.\n"
+ );
+ delete $heap->{client};
+}
+
+### The client handler has flushed its response to the socket. We're
+### done with the client connection, so stop the ReadWrite wheel.
+
+sub client_flushed_request {
+ my $heap = $_[HEAP];
+ DEBUG and
+ warn( "Client handler $$/", $_[SESSION]->ID,
+ " flushed its response.\n",
+ "Client handler $$/", $_[SESSION]->ID, " is shutting down.\n"
+ );
+ delete $heap->{client};
+}
+
+sub usage {
+ my $name = shift;
+ die "Usage:\n\n freeside-$name user\n";
+}
+
+1;
use HTML::FormatText;
use HTML::Defang;
use JSON::XS;
-# use XMLRPC::Transport::HTTP;
-# use XMLRPC::Lite; # for XMLRPC::Serializer
use MIME::Base64;
use IO::Handle;
use IO::File;
use FS::usage_class;
use FS::payment_gateway;
use FS::agent_payment_gateway;
- use FS::XMLRPC;
use FS::payby;
use FS::cdr;
use FS::cdr_batch;
use FS::part_fee;
use FS::cust_bill_pkg_fee;
use FS::part_fee_msgcat;
+ use FS::part_fee_usage;
# Sammath Naur
if ( $FS::Mason::addl_handler_use ) {
}
# Logging
- if ( $conf->exists('log_sent_mail') and $options{'custnum'} ) {
+ if ( $conf->exists('log_sent_mail') ) {
my $cust_msg = FS::cust_msg->new({
'env_from' => $options{'from'},
'env_to' => join(', ', @to),
'custnum' => $options{'custnum'},
'msgnum' => $options{'msgnum'},
'status' => ($error ? 'failed' : 'sent'),
+ 'msgtype' => $options{'msgtype'},
});
$cust_msg->insert; # ignore errors
}
my $me = '[FS::Misc::generate_email]';
- my @fields = qw(from to bcc subject custnum msgnum);
+ my @fields = qw(from to bcc subject custnum msgnum msgtype);
my %return;
@return{@fields} = @args{@fields};
sub credits {
my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_;
$self->scalar_sql("
- SELECT SUM(amount)
+ SELECT SUM(cust_credit.amount)
FROM cust_credit
LEFT JOIN cust_main USING ( custnum )
WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum).
'use_override': for line items generated by an add-on package, use the class
of the add-on rather than the base package.
-'freq': limit to packages with this frequency. Currently uses the part_pkg
-frequency, so term discounted packages may give odd results.
-
'distribute': for non-monthly recurring charges, ignore the invoice
date. Instead, consider the line item's starting/ending dates. Determine
the fraction of the line item duration that falls within the specified
LEFT JOIN cust_main USING ( custnum )
LEFT JOIN cust_pkg USING ( pkgnum )
LEFT JOIN part_pkg USING ( pkgpart )
- LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart';
+ LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart
+ LEFT JOIN part_fee USING ( feepart )';
sub cust_bill_pkg_setup {
my $self = shift;
$agentnum ||= $opt{'agentnum'};
my @where = (
- 'pkgnum != 0',
+ '(pkgnum != 0 OR feepart IS NOT NULL)',
$self->with_classnum($opt{'classnum'}, $opt{'use_override'}),
$self->with_report_option(%opt),
$self->in_time_period_and_agent($speriod, $eperiod, $agentnum),
my $cust_bill_pkg = $opt{'project'} ? 'v_cust_bill_pkg' : 'cust_bill_pkg';
my @where = (
- 'pkgnum != 0',
+ '(pkgnum != 0 OR feepart IS NOT NULL)',
$self->with_classnum($opt{'classnum'}, $opt{'use_override'}),
$self->with_report_option(%opt),
);
$item_usage = 'usage'; #already calculated
}
else {
- $item_usage = '( SELECT COALESCE(SUM(amount),0)
+ $item_usage = '( SELECT COALESCE(SUM(cust_bill_pkg_detail.amount),0)
FROM cust_bill_pkg_detail
WHERE cust_bill_pkg_detail.billpkgnum = cust_bill_pkg.billpkgnum )';
}
my $recur_fraction = '';
if ( $opt{'distribute'} ) {
+ $where[0] = 'pkgnum != 0'; # specifically exclude fees
push @where, "cust_main.agentnum = $agentnum" if $agentnum;
push @where,
"$cust_bill_pkg.sdate < $eperiod",
sub cust_bill_pkg_detail {
my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_;
- my @where = ( "cust_bill_pkg.pkgnum != 0" );
+ my @where =
+ ( "(cust_bill_pkg.pkgnum != 0 OR cust_bill_pkg.feepart IS NOT NULL)" );
push @where, 'cust_main.refnum = '. $opt{'refnum'} if $opt{'refnum'};
;
if ( $opt{'distribute'} ) {
- # then limit according to the usage time, not the billing date
+ # exclude fees
+ $where[0] = 'cust_bill_pkg.pkgnum != 0';
+ # and limit according to the usage time, not the billing date
push @where, $self->in_time_period_and_agent($speriod, $eperiod, $agentnum,
'cust_bill_pkg_detail.startdate'
);
);
}
- my $total_sql = " SELECT SUM(amount) ";
+ my $total_sql = " SELECT SUM(cust_bill_pkg_detail.amount) ";
$total_sql .=
" / CASE COUNT(cust_pkg.*) WHEN 0 THEN 1 ELSE COUNT(cust_pkg.*) END "
LEFT JOIN cust_pkg ON cust_bill_pkg.pkgnum = cust_pkg.pkgnum
LEFT JOIN part_pkg USING ( pkgpart )
LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart
+ LEFT JOIN part_fee USING ( feepart )
WHERE ".join( ' AND ', grep $_, @where );
$self->scalar_sql($total_sql);
@$classnum = grep /^\d+$/, @$classnum;
my $in = 'IN ('. join(',', @$classnum). ')';
- if ( $use_override ) {
- "(
+ my $expr = "
( COALESCE(part_pkg.classnum, 0) $in AND pkgpart_override IS NULL)
- OR ( COALESCE(override.classnum, 0) $in AND pkgpart_override IS NOT NULL )
- )";
- } else {
- "COALESCE(part_pkg.classnum, 0) $in";
+ OR ( COALESCE(part_fee.classnum, 0) $in AND feepart IS NOT NULL )";
+ if ( $use_override ) {
+ $expr .= "
+ OR ( COALESCE(override.classnum, 0) $in AND pkgpart_override IS NOT NULL )";
}
+ "( $expr )";
}
sub with_usageclass {
# sdate/edate overlapping the ROI, for performance
"INSERT INTO v_cust_bill_pkg (
SELECT cust_bill_pkg.*,
- (SELECT COALESCE(SUM(amount),0) FROM cust_bill_pkg_detail
+ (SELECT COALESCE(SUM(cust_bill_pkg_detail.amount),0)
+ FROM cust_bill_pkg_detail
WHERE cust_bill_pkg_detail.billpkgnum = cust_bill_pkg.billpkgnum),
cust_bill._date,
cust_pkg.expire
'eventnum', 'int', '', '', '', '',
'billpkgnum', 'int', 'NULL', '', '', '',
'feepart', 'int', '', '', '', '',
+ 'nextbill', 'char', 'NULL', 1, '', '',
],
'primary_key' => 'eventfeenum', # I'd rather just use eventnum
'unique' => [ [ 'billpkgnum' ], [ 'eventnum' ] ], # one-to-one link
'gatewaynum', 'int', 'NULL', '', '', '',
#'cust_balance', @money_type, '', '',
'paynum', 'int', 'NULL', '', '', '',
+ 'void_paynum', 'int', 'NULL', '', '', '',
'jobnum', 'bigint', 'NULL', '', '', '',
'invnum', 'int', 'NULL', '', '', '',
'manual', 'char', 'NULL', 1, '', '',
{ columns => [ 'paynum' ],
table => 'cust_pay',
},
+ { columns => [ 'void_paynum' ],
+ table => 'cust_pay_void',
+ references => [ 'paynum' ],
+ },
{ columns => [ 'jobnum' ],
table => 'queue',
},
],
},
+ 'part_fee_usage' => {
+ 'columns' => [
+ 'feepartusagenum','serial', '', '', '', '',
+ 'feepart', 'int', '', '', '', '',
+ 'classnum', 'int', '', '', '', '',
+ 'amount', @money_type, '', '',
+ 'percent', 'decimal', '', '7,4', '', '',
+ ],
+ 'primary_key' => 'feepartusagenum',
+ 'unique' => [ [ 'feepart', 'classnum' ] ],
+ 'index' => [],
+ 'foreign_keys' => [
+ { columns => [ 'feepart' ],
+ table => 'part_fee',
+ },
+ { columns => [ 'classnum' ],
+ table => 'usage_class',
+ },
+ ],
+ },
'part_pkg_link' => {
'columns' => [
'unique' => [ [ 'blocknum', 'routernum' ] ],
'index' => [],
'foreign_keys' => [
- { columns => [ 'routernum' ],
- table => 'router',
- },
+ #{ columns => [ 'routernum' ],
+ # table => 'router',
+ #},
{ columns => [ 'agentnum' ],
table => 'agent',
},
'cust_msg' => {
'columns' => [
'custmsgnum', 'serial', '', '', '', '',
- 'custnum', 'int', '', '', '', '',
+ 'custnum', 'int', 'NULL', '', '', '',
'msgnum', 'int', 'NULL', '', '', '',
'_date', @date_type, '', '',
'env_from', 'varchar', 'NULL', 255, '', '',
'body', 'blob', 'NULL', '', '', '',
'error', 'varchar', 'NULL', 255, '', '',
'status', 'varchar', '',$char_d, '', '',
+ 'msgtype', 'varchar', 'NULL', 16, '', '',
],
'primary_key' => 'custmsgnum',
'unique' => [ ],
my( $self, $locale ) = @_;
if ( $self->pkgnum > 0 ) {
- $self->itemdesc || $self->part_pkg->pkg_locale($locale);
+ return $self->itemdesc if $self->itemdesc;
+ my $part_pkg = $self->part_pkg or return 'UNKNOWN';
+ return $part_pkg->pkg_locale($locale);
+
} elsif ( $self->feepart ) {
- $self->part_fee->itemdesc_locale($locale);
+ return $self->part_fee->itemdesc_locale($locale);
+
} else { # by the process of elimination it must be a tax
my $desc = $self->itemdesc || 'Tax';
$desc .= ' '. $self->itemcomment if $self->itemcomment =~ /\S/;
- $desc;
+ return $desc;
}
+
}
=item time_period_pretty PART_PKG, AGENTNUM
warn "$me _items_cust_bill_pkg cust_bill_pkg is quotation_pkg\n"
if $DEBUG > 1;
+ # quotation_pkgs are never fees, so don't worry about the case where
+ # part_pkg is undefined
if ( $cust_bill_pkg->setup != 0 ) {
my $description = $desc;
};
}
- } elsif ( $cust_bill_pkg->pkgnum > 0 ) {
+ } elsif ( $cust_bill_pkg->pkgnum > 0 ) { # and it's not a quotation_pkg
warn "$me _items_cust_bill_pkg cust_bill_pkg is non-tax\n"
if $DEBUG > 1;
} # recurring or usage with recurring charge
- } else { #pkgnum tax or one-shot line item (??)
+ } else { # taxes and fees
warn "$me _items_cust_bill_pkg cust_bill_pkg is tax\n"
if $DEBUG > 1;
- if ( $cust_bill_pkg->setup != 0 ) {
- push @b, {
- 'description' => $desc,
- 'amount' => sprintf("%.2f", $cust_bill_pkg->setup),
- };
- }
- if ( $cust_bill_pkg->recur != 0 ) {
- push @b, {
- 'description' => "$desc (".
- $self->time2str_local('short', $cust_bill_pkg->sdate). ' - '.
- $self->time2str_local('short', $cust_bill_pkg->edate). ')',
- 'amount' => sprintf("%.2f", $cust_bill_pkg->recur),
- };
- }
+ # items of this kind should normally not have sdate/edate.
+ push @b, {
+ 'description' => $desc,
+ 'amount' => sprintf('%.2f', $cust_bill_pkg->setup
+ + $cust_bill_pkg->recur)
+ };
- }
+ } # if quotation / package line item / other line item
- }
+ } # foreach $display
$discount_show_always = ($cust_bill_pkg->cust_bill_pkg_discount
&& $conf->exists('discount-show-always'));
+++ /dev/null
- package FS::XMLRPC;
-
-use strict;
-use vars qw( $DEBUG );
-use Frontier::RPC2;
-
-# Instead of 'use'ing freeside modules on the fly below, just preload them now.
-use FS;
-use FS::CGI;
-use FS::Conf;
-use FS::Record;
-use FS::cust_main;
-
-use Data::Dumper;
-
-$DEBUG = 0;
-
-=head1 NAME
-
-FS::XMLRPC - Object methods for handling XMLRPC requests
-
-=head1 SYNOPSIS
-
- use FS::XMLRPC;
-
- $xmlrpc = new FS::XMLRPC;
-
- ($error, $response_xml) = $xmlrpc->serve($request_xml);
-
-=head1 DESCRIPTION
-
-The FS::XMLRPC object is a mechanisim to access read-only data from freeside's subroutines. It does not, at least not at this point, give you the ability to access methods of freeside objects remotely. It can, however, be used to call subroutines such as FS::cust_main::smart_search and FS::Record::qsearch.
-
-See the serve method below for calling syntax.
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-Provides a FS::XMLRPC object used to handle incoming XMLRPC requests.
-
-=cut
-
-sub new {
-
- my $class = shift;
- my $self = {};
- bless($self, $class);
-
- $self->{_coder} = new Frontier::RPC2;
-
- return $self;
-
-}
-
-=item serve REQUEST_XML_SCALAR
-
-The serve method takes a scalar containg an XMLRPC request for one of freeside's subroutines (not object methods). Parameters passed in the 'methodCall' will be passed as a list to the subroutine untouched. The return value of the called subroutine _must_ be a freeside object reference (eg. qsearchs) or a list of freeside object references (eg. qsearch, smart_search), _and_, the object(s) returned must support the hashref method. This will be checked first by calling UNIVERSAL::can('FS::class::subroutine', 'hashref').
-
-Return value is an XMLRPC methodResponse containing the results of the call. The result of the subroutine call itself will be coded in the methodResponse as an array of structs, regardless of whether there was many or a single object returned. In other words, after you decode the response, you'll always have an array.
-
-=cut
-
-sub serve {
-
- my ($self, $request_xml) = (shift, shift);
- my $response_xml;
-
- my $coder = $self->{_coder};
- my $call = $coder->decode($request_xml);
-
- warn "Got methodCall with method_name='" . $call->{method_name} . "'"
- if $DEBUG;
-
- $response_xml = $coder->encode_response(&_serve($call->{method_name}, $call->{value}));
-
- return ('', $response_xml);
-
-}
-
-sub _serve { #Subroutine, not method
-
- my ($method_name, $params) = (shift, shift);
-
-
- #die 'Called _serve without parameters' unless ref($params) eq 'ARRAY';
- $params = [] unless (ref($params) eq 'ARRAY');
-
- if ($method_name =~ /^(\w+)\.(\w+)/) {
-
- #my ($class, $sub) = split(/\./, $method_name);
- my ($class, $sub) = ($1, $2);
- my $fssub = "FS::${class}::${sub}";
- warn "fssub: ${fssub}" if $DEBUG;
- warn "params: " . Dumper($params) if $DEBUG;
-
- my @result;
-
- if ($class eq 'Conf') { #Special case for FS::Conf because we need an obj.
-
- if ($sub eq 'config') {
- my $conf = new FS::Conf;
- @result = ($conf->config(@$params));
- } else {
- warn "FS::XMLRPC: Can't call undefined subroutine '${fssub}'";
- }
-
- } else {
-
- unless (UNIVERSAL::can("FS::${class}", $sub)) {
- warn "FS::XMLRPC: Can't call undefined subroutine '${fssub}'";
- # Should we encode an error in the response,
- # or just break silently to the remote caller and complain locally?
- return [];
- }
-
- eval {
- no strict 'refs';
- my $fssub = "FS::${class}::${sub}";
- @result = (&$fssub(@$params));
- };
-
- if ($@) {
- warn "FS::XMLRPC: Error while calling '${fssub}': $@";
- return [];
- }
-
- }
-
- if ( scalar(@result) == 1 && ref($result[0]) eq 'HASH' ) {
- return $result[0];
- } elsif (grep { UNIVERSAL::can($_, 'hashref') ? 0 : 1 } @result) {
- #warn "FS::XMLRPC: One or more objects returned from '${fssub}' doesn't " .
- # "support the 'hashref' method.";
-
- # If they're not FS::Record decendants, just return the results unmap'd?
- # This is more flexible, but possibly more error-prone.
- return [ @result ];
- } else {
- return [ map { $_->hashref } @result ];
- }
- } elsif ($method_name eq 'version') {
- return [ $FS::VERSION ];
- } # else...
-
- warn "Unhandled XMLRPC request '${method_name}'";
- return {};
-
-}
-
-=head1 BUGS
-
-Probably lots.
-
-=head1 SEE ALSO
-
-L<Frontier::RPC2>.
-
-=cut
-
-1;
-
--- /dev/null
+package FS::XMLRPC_Lite;
+
+use XMLRPC::Lite;
+
+use XMLRPC::Transport::HTTP;
+
+#XXX submit patch to SOAP::Lite
+
+package XMLRPC::Transport::HTTP::Server;
+
+@XMLRPC::Transport::HTTP::Server::ISA = qw(SOAP::Transport::HTTP::Server);
+
+sub initialize; *initialize = \&XMLRPC::Server::initialize;
+sub make_fault; *make_fault = \&XMLRPC::Transport::HTTP::CGI::make_fault;
+sub make_response; *make_response = \&XMLRPC::Transport::HTTP::CGI::make_response;
+
+1;
my %return = (
'from' => $args{'from'},
'subject' => ($args{'subject'} || $self->email_subject),
+ 'custnum' => $self->custnum,
+ 'msgtype' => 'invoice',
);
$args{'unsquelch_cdr'} = $conf->exists('voip-cdr_email');
}
}
+sub tax_location {
+ my $self = shift;
+ FS::cust_location->by_key($self->tax_locationnum);
+}
+
+=item part_X
+
+Returns the L<FS::part_pkg> or L<FS::part_fee> object that defines this
+charge. If called on a tax line, returns nothing.
+
+=cut
+
+sub part_X {
+ my $self = shift;
+ if ( $self->override_pkgpart ) {
+ return FS::part_pkg->by_key($self->override_pkgpart);
+ } elsif ( $self->pkgnum ) {
+ return $self->cust_pkg->part_pkg;
+ } elsif ( $self->feepart ) {
+ return $self->part_fee;
+ } else {
+ return;
+ }
+}
+
=back
=head1 CLASS METHODS
=head1 DESCRIPTION
An FS::cust_bill_pkg_fee object records the origin of a fee.
-. FS::cust_bill_pkg_fee inherits from
-FS::Record. The following fields are currently supported:
+FS::cust_bill_pkg_fee inherits from FS::Record. The following fields
+are currently supported:
=over 4
my $error =
$self->ut_numbern('billpkgfeenum')
|| $self->ut_number('billpkgnum')
- || $self->ut_foreign_key('origin_invnum', 'cust_bill', 'invnum')
- || $self->ut_foreign_keyn('origin_billpkgnum', 'cust_bill_pkg', 'billpkgnum')
+ || $self->ut_foreign_key('base_invnum', 'cust_bill', 'invnum')
+ || $self->ut_foreign_keyn('base_billpkgnum', 'cust_bill_pkg', 'billpkgnum')
|| $self->ut_money('amount')
;
return $error if $error;
# recalculate taxes with new amounts
$taxlisthash{$invnum} ||= {};
- my $part_pkg = $cust_bill_pkg->part_pkg;
- $cust_main->_handle_taxes( $part_pkg,
- $taxlisthash{$invnum},
- $cust_bill_pkg,
- $cust_bill_pkg->cust_pkg,
- $cust_bill_pkg->cust_bill->_date, #invoice time
- $cust_bill_pkg->cust_pkg->pkgpart,
- );
+ my $part_pkg = $cust_bill_pkg->part_pkg
+ if $cust_bill_pkg->pkgpart_override;
+ $cust_main->_handle_taxes( $taxlisthash{$invnum}, $cust_bill_pkg );
}
###
# we still have to deal with the possibility that the tax links don't
# cover the whole amount of tax because of an incomplete upgrade...
- if ($amount > 0) {
+ if ($amount > 0.005) {
$cust_credit_bill{$invnum} += $amount;
push @{ $cust_credit_bill_pkg{$invnum} },
new FS::cust_credit_bill_pkg {
'billpkgnum' => $tax_item->billpkgnum,
- 'amount' => $amount,
+ 'amount' => sprintf('%.2f', $amount),
'setuprecur' => 'setup',
};
'amount' => sprintf('%.2f', 0-$amount),
};
- my $error = $cust_tax_exempt_pkg->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "error inserting cust_tax_exempt_pkg: $error";
+ if ( $cust_tax_exempt_pkg->cust_main_county ) {
+
+ my $error = $cust_tax_exempt_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error inserting cust_tax_exempt_pkg: $error";
+ }
+
}
+
} #foreach $exemption
}
=item feepart - key of the fee definition (L<FS::part_fee>).
+=item nextbill - 'Y' if the fee should be charged on the customer's next
+bill, rather than causing a bill to be produced immediately.
+
=back
=head1 METHODS
|| $self->ut_foreign_key('eventnum', 'cust_event', 'eventnum')
|| $self->ut_foreign_keyn('billpkgnum', 'cust_bill_pkg', 'billpkgnum')
|| $self->ut_foreign_key('feepart', 'part_fee', 'feepart')
+ || $self->ut_flag('nextbill')
;
return $error if $error;
=item by_cust CUSTNUM[, PARAMS]
Finds all cust_event_fee records belonging to the customer CUSTNUM. Currently
-fee events can be cust_main or cust_bill events; this will return both.
+fee events can be cust_main, cust_pkg, or cust_bill events; this will return
+all of them.
PARAMS can be additional params to pass to qsearch; this really only works
for 'hashref' and 'order_by'.
extra_sql => "$where eventtable = 'cust_bill' ".
"AND cust_bill.custnum = $custnum",
%params
+ }),
+ qsearch({
+ table => 'cust_event_fee',
+ addl_from => 'JOIN cust_event USING (eventnum) ' .
+ 'JOIN part_event USING (eventpart) ' .
+ 'JOIN cust_pkg ON (cust_event.tablenum = cust_pkg.pkgnum)',
+ extra_sql => "$where eventtable = 'cust_pkg' ".
+ "AND cust_pkg.custnum = $custnum",
+ %params
})
}
my @cust_bill_pkg = _omit_zero_value_bundles(@{ $cust_bill_pkg{$pass} });
- next unless @cust_bill_pkg; #don't create an invoice w/o line items
-
warn "$me billing pass $pass\n"
#.Dumper(\@cust_bill_pkg)."\n"
if $DEBUG > 2;
hashref => { 'billpkgnum' => '' }
);
warn "$me found pending fee events:\n".Dumper(\@pending_event_fees)."\n"
- if @pending_event_fees;
+ if @pending_event_fees and $DEBUG > 1;
+
+ # determine whether to generate an invoice
+ my $generate_bill = scalar(@cust_bill_pkg) > 0;
+
+ foreach my $event_fee (@pending_event_fees) {
+ $generate_bill = 1 unless $event_fee->nextbill;
+ }
+
+ # don't create an invoice with no line items, or where the only line
+ # items are fees that are supposed to be held until the next invoice
+ next if !$generate_bill;
+ # calculate fees...
my @fee_items;
foreach my $event_fee (@pending_event_fees) {
my $object = $event_fee->cust_event->cust_X;
+ my $part_fee = $event_fee->part_fee;
my $cust_bill;
- if ( $object->isa('FS::cust_main') ) {
+ if ( $object->isa('FS::cust_main') or $object->isa('FS::cust_pkg') ) {
# Not the real cust_bill object that will be inserted--in particular
# there are no taxes yet. If you want to charge a fee on the total
# invoice amount including taxes, you have to put the fee on the next
'charged' => ${ $total_setup{$pass} } +
${ $total_recur{$pass} },
});
+
+ # If this is a package event, only apply the fee to line items
+ # from that package.
+ if ($object->isa('FS::cust_pkg')) {
+ $cust_bill->set('cust_bill_pkg',
+ [ grep { $_->pkgnum == $object->pkgnum } @cust_bill_pkg ]
+ );
+ }
+
} elsif ( $object->isa('FS::cust_bill') ) {
# simple case: applying the fee to a previous invoice (late fee,
# etc.)
$cust_bill = $object;
}
- my $part_fee = $event_fee->part_fee;
# if the fee def belongs to a different agent, don't charge the fee.
# event conditions should prevent this, but just in case they don't,
# skip the fee.
# also skip if it's disabled
next if $part_fee->disabled eq 'Y';
# calculate the fee
- my $fee_item = $event_fee->part_fee->lineitem($cust_bill);
+ my $fee_item = $part_fee->lineitem($cust_bill) or next;
# link this so that we can clear the marker on inserting the line item
$fee_item->set('cust_event_fee', $event_fee);
push @fee_items, $fee_item;
+
}
+
+ # add fees to the invoice
foreach my $fee_item (@fee_items) {
push @cust_bill_pkg, $fee_item;
my $fee_location = $self->ship_location; # I think?
my $error = $self->_handle_taxes(
- $part_fee,
$taxlisthash{$pass},
$fee_item,
- $fee_location,
- $options{invoice_time},
- {} # no options
+ location => $fee_location
);
return $error if $error;
# handle taxes
###
- my $error = $self->_handle_taxes(
- $part_pkg,
- $taxlisthash,
- $cust_bill_pkg,
- $cust_location,
- $options{invoice_time},
- \%options # I have serious objections to this
- );
+ my $error = $self->_handle_taxes( $taxlisthash, $cust_bill_pkg );
return $error if $error;
$cust_bill_pkg->set_display(
return @transfers;
}
-=item _handle_taxes PART_ITEM TAXLISTHASH CUST_BILL_PKG CUST_LOCATION TIME [ OPTIONS ]
+=item handle_taxes TAXLISTHASH CUST_BILL_PKG [ OPTIONS ]
This is _handle_taxes. It's called once for each cust_bill_pkg generated
-from _make_lines, along with the part_pkg (or part_fee), cust_location,
-invoice time, a flag indicating whether the package is being canceled, and a
-partridge in a pear tree.
+from _make_lines.
-The most important argument is 'taxlisthash'. This is shared across the
-entire invoice. It looks like this:
+TAXLISTHASH is a hashref shared across the entire invoice. It looks like
+this:
{
'cust_main_county 1001' => [ [FS::cust_main_county], ... ],
'cust_main_county 1002' => [ [FS::cust_main_county], ... ],
the 'taxline' method to calculate the amount of the tax. This doesn't
happen until calculate_taxes, though.
+OPTIONS may include:
+- part_item: a part_pkg or part_fee object to be used as the package/fee
+ definition.
+- location: a cust_location to be used as the billing location.
+
+If not supplied, part_item will be inferred from the pkgnum or feepart of the
+cust_bill_pkg, and location from the pkgnum (or, for fees, the invnum and
+the customer's default service location).
+
=cut
sub _handle_taxes {
my $self = shift;
- my $part_item = shift;
my $taxlisthash = shift;
my $cust_bill_pkg = shift;
- my $location = shift;
- my $invoice_time = shift;
- my $options = shift;
+ my %options = @_;
+
+ # at this point I realize that we have enough information to infer all this
+ # stuff, instead of passing around giant honking argument lists
+ my $location = $options{location} || $cust_bill_pkg->tax_location;
+ my $part_item = $options{part_item} || $cust_bill_pkg->part_X;
local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG;
my @classes;
#push @classes, $cust_bill_pkg->usage_classes if $cust_bill_pkg->type eq 'U';
push @classes, $cust_bill_pkg->usage_classes if $cust_bill_pkg->usage;
- # debatable
- push @classes, 'setup' if ($cust_bill_pkg->setup && !$options->{cancel});
- push @classes, 'recur' if ($cust_bill_pkg->recur && !$options->{cancel});
+ push @classes, 'setup' if $cust_bill_pkg->setup;
+ push @classes, 'recur' if $cust_bill_pkg->recur;
my $exempt = $conf->exists('cust_class-tax_exempt')
? ( $self->cust_class ? $self->cust_class->tax : '' )
warn "adding $totname to taxed taxes\n" if $DEBUG > 2;
# calculate the tax amount that the tax_on_tax will apply to
my $hashref_or_error =
- $tax_object->taxline( $localtaxlisthash{$tax},
- 'custnum' => $self->custnum,
- 'invoice_time' => $invoice_time,
- );
+ $tax_object->taxline( $localtaxlisthash{$tax} );
return $hashref_or_error
unless ref($hashref_or_error);
=head1 DESCRIPTION
-An FS::cust_msg object represents a template-generated message sent to
-a customer (see L<FS::msg_template>). FS::cust_msg inherits from
-FS::Record. The following fields are currently supported:
+An FS::cust_msg object represents an email message generated by Freeside
+and sent to a customer (see L<FS::msg_template>). FS::cust_msg inherits
+from FS::Record. The following fields are currently supported:
=over 4
=item msgnum - template number
+=item msgtype - the message type
+
=item _date - the time the message was sent
=item env_from - envelope From address
my $error =
$self->ut_numbern('custmsgnum')
- || $self->ut_number('custnum')
- || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
+ || $self->ut_numbern('custnum')
+ || $self->ut_foreign_keyn('custnum', 'cust_main', 'custnum')
|| $self->ut_numbern('msgnum')
|| $self->ut_foreign_keyn('msgnum', 'msg_template', 'msgnum')
|| $self->ut_numbern('_date')
|| $self->ut_anything('body')
|| $self->ut_enum('status', \@statuses)
|| $self->ut_textn('error')
+ || $self->ut_enum('msgtype', [ '',
+ 'invoice',
+ 'receipt',
+ 'admin',
+ ])
;
return $error if $error;
} );
$cust_pay_void->reason(shift) if scalar(@_);
my $error = $cust_pay_void->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
+
+ my $cust_pay_pending =
+ qsearchs('cust_pay_pending', { paynum => $self->paynum });
+ if ( $cust_pay_pending ) {
+ $cust_pay_pending->set('void_paynum', $self->paynum);
+ $cust_pay_pending->set('paynum', '');
+ $error ||= $cust_pay_pending->replace;
}
- $error = $self->delete;
+ $error ||= $self->delete;
+
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
'custnum' => $cust_main->custnum,
};
$error = $queue->insert(
- FS::msg_template->by_key($msgnum)->prepare(
+ FS::msg_template->by_key($msgnum)->prepare(
'cust_main' => $cust_main,
'object' => $self,
'from_config' => 'payment_receipt_from',
- )
+ ),
+ 'msgtype' => 'receipt', # override msg_template's default
);
} elsif ( $conf->exists('payment_receipt_email') ) {
'job' => 'FS::Misc::process_send_generated_email',
'paynum' => $self->paynum,
'custnum' => $cust_main->custnum,
+ 'msgtype' => 'receipt',
};
$error = $queue->insert(
'from' => $conf->config('invoice_from', $cust_main->agentnum),
Payment number (L<FS::cust_pay>) of the completed payment.
+=item void_paynum
+
+Payment number of the payment if it's been voided.
+
=item invnum
Invoice number (L<FS::cust_bill>) to try to apply this payment to.
|| $self->ut_foreign_keyn('paynum', 'cust_pay', 'paynum' )
|| $self->ut_foreign_keyn('pkgnum', 'cust_pkg', 'pkgnum')
|| $self->ut_foreign_keyn('invnum', 'cust_bill', 'invnum')
+ || $self->ut_foreign_keyn('void_paynum', 'cust_pay_void', 'paynum' )
|| $self->ut_flag('manual')
|| $self->ut_numbern('discount_term')
|| $self->payinfo_check() #payby/payinfo/paymask/paydate
map { $_ => $self->get($_) } fields('cust_pay')
} );
my $error = $cust_pay->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
+
+ my $cust_pay_pending =
+ qsearchs('cust_pay_pending', { void_paynum => $self->paynum });
+ if ( $cust_pay_pending ) {
+ $cust_pay_pending->set('paynum', $cust_pay->paynum);
+ $cust_pay_pending->set('void_paynum', '');
+ $error ||= $cust_pay_pending->replace;
}
- $error = $self->delete;
+ $error ||= $self->delete;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
'to' => \@invoicing_list,
'subject' => ( $conf->config('cancelsubject') || 'Cancellation Notice' ),
'body' => [ map "$_\n", $conf->config('cancelmessage') ],
+ 'custnum' => $self->custnum,
+ 'msgtype' => '', #admin?
);
}
#should this do something on errors?
'Package : #'. $self->pkgnum. " (". $self->part_pkg->pkg_comment. ")\n",
( map { "Service : $_\n" } @labels ),
],
+ 'custnum' => $self->custnum,
+ 'msgtype' => 'admin'
);
if ( $error ) {
: ''
),
],
+ 'custnum' => $self->custnum,
+ 'msgtype' => 'admin',
);
if ( $error ) {
);
if( exists($params->{'active'} ) ) {
- # This overrides all the other date-related fields
+ # This overrides all the other date-related fields, and includes packages
+ # that were active at some time during the interval. It excludes:
+ # - packages that were set up after the end of the interval
+ # - packages that were canceled before the start of the interval
+ # - packages that were suspended before the start of the interval
+ # and are still suspended now
my($beginning, $ending) = @{$params->{'active'}};
push @where,
"cust_pkg.setup IS NOT NULL",
"cust_pkg.setup <= $ending",
"(cust_pkg.cancel IS NULL OR cust_pkg.cancel >= $beginning )",
+ "(cust_pkg.susp IS NULL OR cust_pkg.susp >= $beginning )",
"NOT (".FS::cust_pkg->onetime_sql . ")";
}
else {
use vars qw( $DEBUG $me $ignore_quantity $conf $ticket_system );
use Carp;
#use Scalar::Util qw( blessed );
+use List::Util qw( max );
use FS::Conf;
use FS::Record qw( qsearch qsearchs dbh str2time_sql );
use FS::part_pkg;
return "Unknown svcpart" unless $part_svc;
if ( $self->pkgnum && ! $ignore_quantity ) {
- my $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
- return "Unknown pkgnum" unless $cust_pkg;
- ($part_svc) = grep { $_->svcpart == $self->svcpart } $cust_pkg->part_svc;
- return "No svcpart ". $self->svcpart.
- " services in pkgpart ". $cust_pkg->pkgpart
- unless $part_svc || $ignore_quantity;
- return "Already ". $part_svc->get('num_cust_svc'). " ". $part_svc->svc.
+
+ #slightly inefficient since ->pkg_svc will also look it up, but fixing
+ # a much larger perf problem and have bigger fish to fry
+ my $cust_pkg = $self->cust_pkg;
+
+ my $pkg_svc = $self->pkg_svc
+ or return "No svcpart ". $self->svcpart.
+ " services in pkgpart ". $cust_pkg->pkgpart;
+
+ my $num_cust_svc = $cust_pkg->num_cust_svc( $self->svcpart );
+
+ #false laziness w/cust_pkg->part_svc
+ my $num_avail = max( 0, ($cust_pkg->quantity || 1) * $pkg_svc->quantity
+ - $num_cust_svc
+ );
+
+ return "Already $num_cust_svc ". $pkg_svc->part_svc->svc.
" services for pkgnum ". $self->pkgnum
- if !$ignore_quantity && $part_svc->get('num_avail') <= 0 ;
+ if $num_avail <= 0;
+
}
$self->SUPER::check;
my $oldAutoCommit = $FS::UID::AutoCommit;
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
-
- my $error = $self->SUPER::delete;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
-
+
my $pkey = $self->primary_key;
#my $option_table = $self->option_table;
}
}
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
use strict;
use base qw( FS::part_event::Action );
+use FS::Record qw( qsearch );
sub event_stage { 'pre-bill'; }
value_col => 'feepart',
disable_empty => 1,
},
- );
+ ),
+
}
sub default_weight { 10; }
+sub hold_until_bill { 1 }
+
sub do_action {
my( $self, $cust_object, $cust_event ) = @_;
- die "no fee definition selected for event '".$self->event."'\n"
- unless $self->option('feepart');
+ my $feepart = $self->option('feepart')
+ or die "no fee definition selected for event '".$self->event."'\n";
+ my $tablenum = $cust_object->get($cust_object->primary_key);
+
+ # see if there's already a pending fee for this customer/invoice
+ my @existing = qsearch({
+ table => 'cust_event_fee',
+ addl_from => 'JOIN cust_event USING (eventnum)',
+ hashref => { feepart => $feepart,
+ billpkgnum => '' },
+ extra_sql => " AND tablenum = $tablenum",
+ });
+ if (scalar @existing > 0) {
+ warn $self->event." event, object $tablenum: already scheduled\n"
+ if $FS::part_fee::DEBUG;
+ return;
+ }
# mark the event so that the fee will be charged
# the logic for calculating the fee amount is in FS::part_fee
# FS::cust_bill_pkg
my $cust_event_fee = FS::cust_event_fee->new({
'eventnum' => $cust_event->eventnum,
- 'feepart' => $self->option('feepart'),
+ 'feepart' => $feepart,
'billpkgnum' => '',
+ 'nextbill' => $self->hold_until_bill ? 'Y' : '',
});
my $error = $cust_event_fee->insert;
{ 'cust_bill' => 1 };
}
+sub option_fields {
+ (
+ __PACKAGE__->SUPER::option_fields,
+ 'nextbill' => { label => 'Hold fee until the customer\'s next bill',
+ type => 'checkbox',
+ value => 'Y'
+ },
+ )
+}
+
+# it makes sense for this to be optional for previous-invoice fees
+sub hold_until_bill {
+ my $self = shift;
+ $self->option('nextbill');
+}
+
1;
{ 'cust_main' => 1 };
}
+sub hold_until_bill { 1 }
+
# Otherwise identical to cust_bill_fee. We only have a separate event
# because it behaves differently as an invoice event than as a customer
# event, and needs a different description.
package FS::part_event::Action::fee;
+# DEPRECATED; will most likely be removed in 4.x
+
use strict;
use base qw( FS::part_event::Action );
my $part_pkg = FS::part_pkg->new({
taxclass => $self->option('taxclass')
});
- my $error = $cust_main->_handle_taxes(
- FS::part_pkg->new({ taxclass => ($self->option('taxclass') || '') }),
- $taxlisthash,
- $charge,
- FS::cust_pkg->new({custnum => $cust_main->custnum}),
+ my $error = $cust_main->_handle_taxes( $taxlisthash, $charge,
+ location => $cust_main->ship_location,
+ part_item => $part_pkg,
);
if ( $error ) {
warn "error estimating taxes for breakage charge: custnum ".$cust_main->custnum."\n";
--- /dev/null
+package FS::part_event::Action::pkg_fee;
+
+use strict;
+use base qw( FS::part_event::Action::Mixin::fee );
+
+sub description { 'Charge a fee when this package is billed'; }
+
+sub eventtable_hashref {
+ { 'cust_pkg' => 1 };
+}
+
+sub hold_until_bill { 1 }
+
+# Functionally identical to cust_fee.
+
+1;
'link_table' => 'export_nas',
'target_table' => 'nas',
'params' => [],
+ ) || $self->process_m2m(
+ 'link_table' => 'export_svc',
+ 'target_table' => 'part_svc',
+ 'params' => [],
) || $self->SUPER::delete;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
use FS::Record qw(qsearch qsearchs);
use FS::Conf;
use FS::msg_template;
-use FS::Misc qw(send_email);
@ISA = qw(FS::part_export);
use vars qw( $DEBUG );
use FS::Record qw( qsearch qsearchs );
-$DEBUG = 1;
+$DEBUG = 0;
=head1 NAME
sub check {
my $self = shift;
+ $self->set('amount', 0) unless $self->amount;
+ $self->set('percent', 0) unless $self->percent;
+
my $error =
$self->ut_numbern('feepart')
|| $self->ut_textn('comment')
|| $self->ut_floatn('credit_weight')
|| $self->ut_agentnum_acl('agentnum',
[ 'Edit global package definitions' ])
- || $self->ut_moneyn('amount')
- || $self->ut_floatn('percent')
+ || $self->ut_money('amount')
+ || $self->ut_float('percent')
|| $self->ut_moneyn('minimum')
|| $self->ut_moneyn('maximum')
|| $self->ut_flag('limit_credit')
- || $self->ut_enum('basis', [ '', 'charged', 'owed' ])
+ || $self->ut_enum('basis', [ 'charged', 'owed', 'usage' ])
|| $self->ut_enum('setuprecur', [ 'setup', 'recur' ])
;
return $error if $error;
- return "For a percentage fee, the basis must be set"
- if $self->get('percent') > 0 and $self->get('basis') eq '';
-
- if ( ! $self->get('percent') and ! $self->get('limit_credit') ) {
- # then it makes no sense to apply minimum/maximum
- $self->set('minimum', '');
- $self->set('maximum', '');
- }
if ( $self->get('limit_credit') ) {
$self->set('maximum', '');
}
+ if ( $self->get('basis') eq 'usage' ) {
+ # to avoid confusion, don't also allow charging a percentage
+ $self->set('percent', 0);
+ }
+
$self->SUPER::check;
}
my $money_char = FS::Conf->new->config('money_char') || '$';
my $money = $money_char . '%.2f';
my $percent = '%.1f%%';
- my $string;
+ my $string = '';
if ( $self->amount > 0 ) {
$string = sprintf($money, $self->amount);
}
} elsif ( $self->basis('owed') ) {
$string .= 'unpaid invoice balance';
}
+ } elsif ( $self->basis eq 'usage' ) {
+ if ( $string ) {
+ $string .= " plus \n";
+ }
+ # append per-class descriptions
+ $string .= join("\n", map { $_->explanation } $self->part_fee_usage);
}
+
if ( $self->minimum or $self->maximum or $self->limit_credit ) {
$string .= "\nbut";
if ( $self->minimum ) {
L<FS::cust_bill_pkg_fee> record(s) allocating the fee to the invoice or
its line items, as appropriate.
+If the fee is going to be charged on the upcoming invoice (credit card
+processing fees, postal invoice fees), INVOICE should be an uninserted
+L<FS::cust_bill> object where the 'cust_bill_pkg' property is an arrayref
+of the non-fee line items that will appear on the invoice.
+
=cut
sub lineitem {
my $self = shift;
my $cust_bill = shift;
+ my $cust_main = $cust_bill->cust_main;
my $amount = 0 + $self->get('amount');
my $total_base; # sum of base line items
warn "Calculating fee: ".$self->itemdesc." on ".
($cust_bill->invnum ? "invoice #".$cust_bill->invnum : "current invoice").
"\n" if $DEBUG;
- if ( $self->percent > 0 and $self->basis ne '' ) {
- warn $self->percent . "% of amount ".$self->basis.")\n"
- if $DEBUG;
-
- # $total_base: the total charged/owed on the invoice
- # %item_base: billpkgnum => fraction of base amount
- if ( $cust_bill->invnum ) {
- my $basis = $self->basis;
- $total_base = $cust_bill->$basis; # "charged", "owed"
+ my $basis = $self->basis;
+
+ # $total_base: the total charged/owed on the invoice
+ # %item_base: billpkgnum => fraction of base amount
+ if ( $cust_bill->invnum ) {
- # calculate the fee on an already-inserted past invoice. This may have
- # payments or credits, so if basis = owed, we need to consider those.
+ # calculate the fee on an already-inserted past invoice. This may have
+ # payments or credits, so if basis = owed, we need to consider those.
+ @items = $cust_bill->cust_bill_pkg;
+ if ( $basis ne 'usage' ) {
+
+ $total_base = $cust_bill->$basis; # "charged", "owed"
my $basis_sql = $basis.'_sql';
my $sql = 'SELECT ' . FS::cust_bill_pkg->$basis_sql .
' FROM cust_bill_pkg WHERE billpkgnum = ?';
- @items = $cust_bill->cust_bill_pkg;
@item_base = map { FS::Record->scalar_sql($sql, $_->billpkgnum) }
@items;
- } else {
- # the fee applies to _this_ invoice. It has no payments or credits, so
- # "charged" and "owed" basis are both just the invoice amount, and
- # the line item amounts (setup + recur)
+
+ $amount += $total_base * $self->percent / 100;
+ }
+ } else {
+ # the fee applies to _this_ invoice. It has no payments or credits, so
+ # "charged" and "owed" basis are both just the invoice amount, and
+ # the line item amounts (setup + recur)
+ @items = @{ $cust_bill->get('cust_bill_pkg') };
+ if ( $basis ne 'usage' ) {
$total_base = $cust_bill->charged;
- @items = @{ $cust_bill->get('cust_bill_pkg') };
@item_base = map { $_->setup + $_->recur }
@items;
- }
- $amount += $total_base * $self->percent / 100;
+ $amount += $total_base * $self->percent / 100;
+ }
}
+ if ( $basis eq 'usage' ) {
+
+ my %part_fee_usage = map { $_->classnum => $_ } $self->part_fee_usage;
+
+ foreach my $item (@items) { # cust_bill_pkg objects
+ my $usage_fee = 0;
+ $item->regularize_details;
+ my $details;
+ if ( $item->billpkgnum ) {
+ $details = [
+ qsearch('cust_bill_pkg_detail', { billpkgnum => $item->billpkgnum })
+ ];
+ } else {
+ $details = $item->get('details') || [];
+ }
+ foreach my $d (@$details) {
+ # if there's a usage fee defined for this class...
+ next if $d->amount eq '' # not a real usage detail
+ or $d->amount == 0 # zero charge, probably shouldn't charge fee
+ ;
+ my $p = $part_fee_usage{$d->classnum} or next;
+ $usage_fee += ($d->amount * $p->percent / 100)
+ + $p->amount;
+ # we'd create detail records here if we were doing that
+ }
+ # bypass @item_base entirely
+ push @item_fee, $usage_fee;
+ $amount += $usage_fee;
+ }
+
+ } # if $basis eq 'usage'
+
if ( $self->minimum ne '' and $amount < $self->minimum ) {
warn "Applying mininum fee\n" if $DEBUG;
$amount = $self->minimum;
my $maximum = $self->maximum;
if ( $self->limit_credit ) {
- my $balance = $cust_bill->cust_main;
+ my $balance = $cust_bill->cust_main->balance;
if ( $balance >= 0 ) {
- $maximum = 0;
+ warn "Credit balance is zero, so fee is zero" if $DEBUG;
+ return; # don't bother doing estimated tax, etc.
} elsif ( -1 * $balance < $maximum ) {
$maximum = -1 * $balance;
}
setup => 0,
recur => 0,
});
+
+ if ( $maximum and $self->taxable ) {
+ warn "Estimating taxes on fee.\n" if $DEBUG;
+ # then we need to estimate tax to respect the maximum
+ # XXX currently doesn't work with external (tax_rate) taxes
+ # or batch taxes, obviously
+ my $taxlisthash = {};
+ my $error = $cust_main->_handle_taxes(
+ $taxlisthash,
+ $cust_bill_pkg,
+ location => $cust_main->ship_location
+ );
+ my $total_rate = 0;
+ # $taxlisthash: tax identifier => [ cust_main_county, cust_bill_pkg... ]
+ my @taxes = map { $_->[0] } values %$taxlisthash;
+ foreach (@taxes) {
+ $total_rate += $_->tax;
+ }
+ if ($total_rate > 0) {
+ my $max_cents = $maximum * 100;
+ my $charge_cents = sprintf('%0.f', $max_cents * 100/(100 + $total_rate));
+ # the actual maximum that we can charge...
+ $maximum = sprintf('%.2f', $charge_cents / 100.00);
+ $amount = $maximum if $amount > $maximum;
+ }
+ } # if $maximum and $self->taxable
+
+ # set the amount that we'll charge
$cust_bill_pkg->set( $self->setuprecur, $amount );
-
+
if ( $self->classnum ) {
my $pkg_category = $self->pkg_class->pkg_category;
$cust_bill_pkg->set('section' => $pkg_category->categoryname)
}
}
}
- # and add them to the cust_bill_pkg
+ }
+ if ( @item_fee ) {
+ # add allocation records to the cust_bill_pkg
for (my $i = 0; $i < scalar(@items); $i++) {
if ( $item_fee[$i] > 0 ) {
push @cust_bill_pkg_fee, FS::cust_bill_pkg_fee->new({
cust_bill_pkg => $cust_bill_pkg,
- base_invnum => $cust_bill->invnum,
+ base_invnum => $cust_bill->invnum, # may be null
amount => $item_fee[$i],
base_cust_bill_pkg => $items[$i], # late resolve
});
}
}
- } else { # if !@item_base
+ } else { # if !@item_fee
# then this isn't a proportional fee, so it just applies to the
# entire invoice.
- # (if it's the current invoice, $cust_bill->invnum is null and that
- # will be fixed later)
push @cust_bill_pkg_fee, FS::cust_bill_pkg_fee->new({
cust_bill_pkg => $cust_bill_pkg,
- base_invnum => $cust_bill->invnum,
+ base_invnum => $cust_bill->invnum, # may be null
amount => $amount,
});
}
--- /dev/null
+package FS::part_fee_usage;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs );
+use FS::Conf;
+
+=head1 NAME
+
+FS::part_fee_usage - Object methods for part_fee_usage records
+
+=head1 SYNOPSIS
+
+ use FS::part_fee_usage;
+
+ $record = new FS::part_fee_usage \%hash;
+ $record = new FS::part_fee_usage { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_fee_usage object is the part of a processing fee definition
+(L<FS::part_fee>) that applies to a specific telephone usage class
+(L<FS::usage_class>). FS::part_fee_usage inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item feepartusagenum - primary key
+
+=item feepart - foreign key to L<FS::part_pkg>
+
+=item classnum - foreign key to L<FS::usage_class>
+
+=item amount - fixed amount to charge per usage record
+
+=item percent - percentage of rated price to charge per usage record
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=cut
+
+sub table { 'part_fee_usage'; }
+
+sub check {
+ my $self = shift;
+
+ $self->set('amount', 0) unless ($self->amount || 0) > 0;
+ $self->set('percent', 0) unless ($self->percent || 0) > 0;
+
+ my $error =
+ $self->ut_numbern('feepartusagenum')
+ || $self->ut_foreign_key('feepart', 'part_fee', 'feepart')
+ || $self->ut_foreign_key('classnum', 'usage_class', 'classnum')
+ || $self->ut_money('amount')
+ || $self->ut_float('percent')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+# silently discard records with percent = 0 and amount = 0
+
+sub insert {
+ my $self = shift;
+ if ( $self->amount > 0 or $self->percent > 0 ) {
+ return $self->SUPER::insert;
+ }
+ '';
+}
+
+sub replace {
+ my ($new, $old) = @_;
+ $old ||= $new->replace_old;
+ if ( $new->amount > 0 or $new->percent > 0 ) {
+ return $new->SUPER::replace($old);
+ } elsif ( $old->feepartusagenum ) {
+ return $old->delete;
+ }
+ '';
+}
+
+=item explanation
+
+Returns a string describing how this fee is calculated.
+
+=cut
+
+sub explanation {
+ my $self = shift;
+ my $string = '';
+ my $money = (FS::Conf->new->config('money_char') || '$') . '%.2f';
+ my $percent = '%.1f%%';
+ if ( $self->amount > 0 ) {
+ $string = sprintf($money, $self->amount);
+ }
+ if ( $self->percent > 0 ) {
+ if ( $string ) {
+ $string .= ' plus ';
+ }
+ $string .= sprintf($percent, $self->percent);
+ $string .= ' of the rated charge';
+ }
+ $string .= ' per '. $self->usage_class->classname . ' call';
+
+ return $string;
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
# 200 mixed debits&credits)
sprintf('%06d', $batchcount). #Entry / Addenda Count
$entry_hash.
- sprintf('%012d', $batchtotal * 100). #Debit total
+ sprintf('%012.0f', $batchtotal * 100). #Debit total
'000000000000'. #Credit total
$origin. #Company Identification (Immediate Origin)
(' 'x19). #Message Authentication Code (19 char blank)
sprintf('%06d', $batchcount + 4). #num of physical blocks on the file..?
sprintf('%08d', $batchcount). #total # of entry detail and addenda
$entry_hash.
- sprintf('%012d', $batchtotal * 100). #Debit total
+ sprintf('%012.0f', $batchtotal * 100). #Debit total
'000000000000'. #Credit total
( ' 'x39 ) #Reserved / blank
use base qw( FS::TemplateItem_Mixin FS::Record );
use strict;
+use FS::Record qw( qsearchs ); #qsearch
+use FS::part_pkg;
use FS::quotation_pkg_discount; #so its loaded when TemplateItem_Mixin needs it
=head1 NAME
$self->SUPER::check;
}
+#it looks redundant with a v4.x+ auto-generated method, but need to override
+# FS::TemplateItem_Mixin's version
+sub part_pkg {
+ my $self = shift;
+ qsearchs('part_pkg', { 'pkgpart' => $self->pkgpart } );
+}
+
sub desc {
my $self = shift;
$self->part_pkg->pkg;
$tax_passtypes{$self->passtype};
}
-=item taxline TAXABLES, [ OPTIONSHASH ]
+=item taxline TAXABLES
Returns a listref of a name and an amount of tax calculated for the list
of packages/amounts referenced by TAXABLES. If an error occurs, a message
sub taxline {
my $self = shift;
+ # this used to accept a hash of options but none of them did anything
+ # so it's been removed.
my $taxables;
- my %opt = ();
if (ref($_[0]) eq 'ARRAY') {
$taxables = shift;
- %opt = @_;
}else{
$taxables = [ @_ ];
#exemptions would be broken in this case
bin/freeside-sqlradius-seconds
bin/freeside-torrus-srvderive
FS.pm
+FS/API.pm
FS/AccessRight.pm
FS/AuthCookieHandler.pm
FS/AuthCookieHandler24.pm
FS/Cron/bill.pm
FS/Cron/vacuum.pm
FS/Daemon.pm
+FS/Daemon/Preforking.pm
FS/Misc.pm
FS/Record.pm
FS/Report.pm
t/cust_bill_pkg_fee.t
FS/part_fee_msgcat.pm
t/part_fee_msgcat.t
+FS/part_fee_usage.pm
+FS/part_fee_usage.t
#!/usr/bin/perl
-#
-# based on http://www.perlmonks.org/?node_id=582781 by Justin Hawkins
-# and http://poe.perl.org/?POE_Cookbook/Web_Server_With_Forking
-
-###
-# modules and constants and variables, oh my
-###
-
-use warnings;
-use strict;
-use constant DEBUG => 1; # Enable much runtime information.
-use constant MAX_PROCESSES => 10; # Total server process count.
-use constant SERVER_PORT => 8080; # Server port.
-use constant TESTING_CHURN => 0; # Randomly test process respawning.
-
-use POE 1.2; # Base features.
-use POE::Filter::HTTPD; # For serving HTTP content.
-use POE::Wheel::ReadWrite; # For socket I/O.
-use POE::Wheel::SocketFactory; # For serving socket connections.
+use FS::Daemon::Preforking qw( freeside_init1 freeside_init2 daemon_run );
use XMLRPC::Transport::HTTP; #SOAP::Transport::HTTP;
use XMLRPC::Lite; # for XMLRPC::Serializer
-use FS::Daemon qw( daemonize1 drop_root logfile daemonize2 );
-use FS::UID qw( adminsuidsetup forksuidsetup dbh );
use FS::Conf;
use FS::ClientAPI qw( load_clientapi_modules );
use FS::ClientAPI_XMLRPC; #FS::SelfService::XMLRPC;
-use FS::TicketSystem;
-
-#freeside
-my $FREESIDE_LOG = "%%%FREESIDE_LOG%%%";
-my $FREESIDE_LOCK = "%%%FREESIDE_LOCK%%%";
-my $lock_file = "$FREESIDE_LOCK/selfservice-xmlrpcd.writelock";
#freeside xmlrpc.cgi
my %typelookup = (
#not utf-8 safe# base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'],
dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'],
- string => [40, sub {1}, 'as_string'],
+ string => [40, sub {1}, 'as_string'],
);
-###
-# freeside init
-###
+use constant ME => 'selfservice-xmlrpcd';
-my $user = shift or die &usage;
-
-$FS::Daemon::NOSIG = 1;
-$FS::Daemon::PID_NEWSTYLE = 1;
-daemonize1('selfservice-xmlrpcd');
-
-POE::Kernel->has_forked(); #daemonize forks...
-
-drop_root();
+#
-adminsuidsetup($user);
+freeside_init1(ME);
load_clientapi_modules;
-logfile("$FREESIDE_LOG/selfservice-xmlrpcd.log");
-
-daemonize2();
+freeside_init2(ME);
FS::ClientAPI::Signup::clear_cache();
my $conf = new FS::Conf;
-
die "not running; selfservice-xmlrpc conf option is off\n"
unless $conf->exists('selfservice-xmlrpc');
-#parent doesn't need to hold a DB connection open
-dbh->disconnect;
-undef $FS::UID::dbh;
-
-###
-# the main loop
-###
-
-server_spawn(MAX_PROCESSES);
-POE::Kernel->run();
-exit;
-
-###
-# the subroutines
-###
-
-### Spawn the main server. This will run as the parent process.
-
-sub server_spawn {
- my ($max_processes) = @_;
-
- POE::Session->create(
- inline_states => {
- _start => \&server_start,
- _stop => \&server_stop,
- do_fork => \&server_do_fork,
- got_error => \&server_got_error,
- got_sig_int => \&server_got_sig_int,
- got_sig_child => \&server_got_sig_child,
- got_connection => \&server_got_connection,
- _child => sub { undef },
- },
- heap => { max_processes => MAX_PROCESSES },
- );
-}
-
-### The main server session has started. Set up the server socket and
-### bookkeeping information, then fork the initial child processes.
-
-sub server_start {
- my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
-
- $heap->{server} = POE::Wheel::SocketFactory->new
- ( BindPort => SERVER_PORT,
- SuccessEvent => "got_connection",
- FailureEvent => "got_error",
- Reuse => "yes",
- );
-
- $kernel->sig( INT => "got_sig_int" );
- $kernel->sig( TERM => "got_sig_int" ); #huh
-
- $heap->{children} = {};
- $heap->{is_a_child} = 0;
-
- warn "Server $$ has begun listening on port ", SERVER_PORT, "\n";
-
- $kernel->yield("do_fork");
-}
-
-### The server session has shut down. If this process has any
-### children, signal them to shutdown too.
-
-sub server_stop {
- my $heap = $_[HEAP];
- DEBUG and warn "Server $$ stopped.\n";
-
- if ( my @children = keys %{ $heap->{children} } ) {
- DEBUG and warn "Server $$ is signaling children to stop.\n";
- kill INT => @children;
- }
-}
-
-### The server session has encountered an error. Shut it down.
-
-sub server_got_error {
- my ( $heap, $syscall, $errno, $error ) = @_[ HEAP, ARG0 .. ARG2 ];
- warn( "Server $$ got $syscall error $errno: $error\n",
- "Server $$ is shutting down.\n",
- );
- delete $heap->{server};
-}
-
-### The server has a need to fork off more children. Only honor that
-### request form the parent, otherwise we would surely "forkbomb".
-### Fork off as many child processes as we need.
-
-sub server_do_fork {
- my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
-
- return if $heap->{is_a_child};
-
- #my $current_children = keys %{ $heap->{children} };
- #for ( $current_children + 2 .. $heap->{max_processes} ) {
- while (scalar(keys %{$heap->{children}}) < $heap->{max_processes}) {
-
- DEBUG and warn "Server $$ is attempting to fork.\n";
-
- my $pid = fork();
-
- unless ( defined($pid) ) {
- DEBUG and
- warn( "Server $$ fork failed: $!\n",
- "Server $$ will retry fork shortly.\n",
- );
- $kernel->delay( do_fork => 1 );
- return;
- }
-
- # Parent. Add the child process to its list.
- if ($pid) {
- $heap->{children}->{$pid} = 1;
- $kernel->sig_child($pid, "got_sig_child");
- next;
- }
-
- # Child. Clear the child process list.
- $kernel->has_forked();
- DEBUG and warn "Server $$ forked successfully.\n";
- $heap->{is_a_child} = 1;
- $heap->{children} = {};
-
- #freeside db connection, etc.
- forksuidsetup($user);
-
- #why isn't this needed ala freeside-selfservice-server??
- #FS::TicketSystem->init();
-
- return;
- }
-}
-
-### The server session received SIGINT. Don't handle the signal,
-### which in turn will trigger the process to exit gracefully.
-
-sub server_got_sig_int {
- my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
- DEBUG and warn "Server $$ received SIGINT/TERM.\n";
-
- if ( my @children = keys %{ $heap->{children} } ) {
- DEBUG and warn "Server $$ is signaling children to stop.\n";
- kill INT => @children;
- }
-
- delete $heap->{server};
- $kernel->sig_handled();
-}
-
-### The server session received a SIGCHLD, indicating that some child
-### server has gone away. Remove the child's process ID from our
-### list, and trigger more fork() calls to spawn new children.
-
-sub server_got_sig_child {
- my ( $kernel, $heap, $child_pid ) = @_[ KERNEL, HEAP, ARG1 ];
-
- return unless delete $heap->{children}->{$child_pid};
-
- DEBUG and warn "Server $$ reaped child $child_pid.\n";
- $kernel->yield("do_fork") if exists $_[HEAP]->{server};
-}
-
-### The server session received a connection request. Spawn off a
-### client handler session to parse the request and respond to it.
-
-sub server_got_connection {
- my ( $heap, $socket, $peer_addr, $peer_port ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
-
- DEBUG and warn "Server $$ received a connection.\n";
-
- POE::Session->create(
- inline_states => {
- _start => \&client_start,
- _stop => \&client_stop,
- got_request => \&client_got_request,
- got_flush => \&client_flushed_request,
- got_error => \&client_got_error,
- _parent => sub { 0 },
- },
- heap => {
- socket => $socket,
- peer_addr => $peer_addr,
- peer_port => $peer_port,
- },
- );
-
- # Gracefully exit if testing process churn.
- delete $heap->{server}
- if TESTING_CHURN and $heap->{is_a_child} and ( rand() < 0.1 );
-}
-
-### The client handler has started. Wrap its socket in a ReadWrite
-### wheel to begin interacting with it.
-
-sub client_start {
- my $heap = $_[HEAP];
-
- $heap->{client} = POE::Wheel::ReadWrite->new
- ( Handle => $heap->{socket},
- Filter => POE::Filter::HTTPD->new(),
- InputEvent => "got_request",
- ErrorEvent => "got_error",
- FlushedEvent => "got_flush",
- );
-
- DEBUG and warn "Client handler $$/", $_[SESSION]->ID, " started.\n";
-}
-
-### The client handler has stopped. Log that fact.
-
-sub client_stop {
- DEBUG and warn "Client handler $$/", $_[SESSION]->ID, " stopped.\n";
-}
-
-### The client handler has received a request. If it's an
-### HTTP::Response object, it means some error has occurred while
-### parsing the request. Send that back and return immediately.
-### Otherwise parse and process the request, generating and sending an
-### HTTP::Response object in response.
-
-sub client_got_request {
- my ( $heap, $request ) = @_[ HEAP, ARG0 ];
-
- forksuidsetup($user) unless dbh && dbh->ping;
+daemon_run( 'port' => 8080, 'handle_request' =>
+ sub {
+ my $request = shift;
my $serializer = new XMLRPC::Serializer(typelookup => \%typelookup);
-> dispatch_to('FS::ClientAPI_XMLRPC')
-> serializer($serializer);
- DEBUG and
- warn "Client handler $$/", $_[SESSION]->ID, " is handling a request.\n";
-
- if ( $request->isa("HTTP::Response") ) {
- $heap->{client}->put($request);
- return;
- }
-
$soap->request($request);
$soap->handle;
- my $response = $soap->response;
-
- $heap->{client}->put($response);
-}
-
-### The client handler received an error. Stop the ReadWrite wheel,
-### which also closes the socket.
-
-sub client_got_error {
- my ( $heap, $operation, $errnum, $errstr ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
- DEBUG and
- warn( "Client handler $$/", $_[SESSION]->ID,
- " got $operation error $errnum: $errstr\n",
- "Client handler $$/", $_[SESSION]->ID, " is shutting down.\n"
- );
- delete $heap->{client};
-}
-### The client handler has flushed its response to the socket. We're
-### done with the client connection, so stop the ReadWrite wheel.
-
-sub client_flushed_request {
- my $heap = $_[HEAP];
- DEBUG and
- warn( "Client handler $$/", $_[SESSION]->ID,
- " flushed its response.\n",
- "Client handler $$/", $_[SESSION]->ID, " is shutting down.\n"
- );
- delete $heap->{client};
-}
-
-sub usage {
- die "Usage:\n\n freeside-selfservice-xmlrpcd user\n";
-}
-
-###
-# the end
-###
+ return $soap->response;
+ }
+);
1;
--- /dev/null
+#!/usr/bin/perl
+
+use FS::Daemon::Preforking qw( freeside_init1 freeside_init2 daemon_run );
+
+use FS::XMLRPC_Lite; #XMLRPC::Lite for XMLRPC::Serializer
+ #and XMLRPC::Transport::HTTP
+
+use FS::Conf;
+
+##use FS::ClientAPI qw( load_clientapi_modules );
+##use FS::ClientAPI_XMLRPC; #FS::SelfService::XMLRPC;
+use FS::API;
+
+#freeside xmlrpc.cgi
+my %typelookup = (
+#not utf-8 safe# base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'],
+ dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'],
+ string => [40, sub {1}, 'as_string'],
+);
+
+use constant ME => 'xmlrpcd';
+
+#
+
+freeside_init1(ME);
+
+#load_clientapi_modules;
+
+freeside_init2(ME);
+
+#FS::ClientAPI::Signup::clear_cache();
+
+my $conf = new FS::Conf;
+die "not running; xmlrpc_api conf option is off\n"
+ unless $conf->exists('xmlrpc_api');
+die "not running; api_shared_secret conf option is not set\n"
+ unless $conf->config('api_shared_secret');
+
+daemon_run( 'port' => 8008, 'handle_request' =>
+ sub {
+ my $request = shift;
+
+ my $serializer = new XMLRPC::Serializer(typelookup => \%typelookup);
+
+ #my $soap = SOAP::Transport::HTTP::Server
+ my $soap = XMLRPC::Transport::HTTP::Server
+ -> new
+ -> dispatch_to('FS::API')
+ -> serializer($serializer);
+
+ $soap->request($request);
+ $soap->handle;
+
+ return $soap->response;
+ }
+);
+
+1;
--- /dev/null
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_fee_usage;
+$loaded=1;
+print "ok 1\n";
#edit the stuff below to have the daemons start
QUEUED_USER=fs_queue
+API_USER = fs_api
SELFSERVICE_USER = fs_selfservice
#never run on the same machine in production!!!
s|%%%FREESIDE_CACHE%%%|${FREESIDE_CACHE}|g;\
" blib/lib/FS/cust_main/*.pm blib/lib/FS/cust_pkg/*.pm;\
perl -p -i -e "\
+ s|%%%FREESIDE_LOG%%%|${FREESIDE_LOG}|g;\
+ " blib/lib/FS/Daemon/*.pm;\
+ perl -p -i -e "\
s|%%%FREESIDE_CONF%%%|${FREESIDE_CONF}|g;\
s|%%%FREESIDE_LOG%%%|${FREESIDE_LOG}|g;\
s|%%%FREESIDE_LOCK%%%|${FREESIDE_LOCK}|g;\
install -o root -g ${INSTALLGROUP} -m 711 init.d/freeside-init ${INIT_FILE}
perl -p -i -e "\
s/%%%QUEUED_USER%%%/${QUEUED_USER}/g;\
+ s/%%%API_USER%%%/${API_USER}/g;\
s/%%%SELFSERVICE_USER%%%/${SELFSERVICE_USER}/g;\
s/%%%SELFSERVICE_MACHINES%%%/${SELFSERVICE_MACHINES}/g;\
" ${INIT_FILE}
+++ /dev/null
-#! /bin/sh
-#
-# start the freeside job queue daemon
-
-#PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-DAEMON=/usr/local/bin/freeside-queued
-NAME=freeside-queued
-DESC="freeside job queue daemon"
-USER="ivan"
-
-test -f $DAEMON || exit 0
-
-set -e
-
-case "$1" in
- start)
- echo -n "Starting $DESC: "
-# start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid -b -m\
-# --exec $DAEMON
- $DAEMON $USER &
- echo "$NAME."
- ;;
- stop)
- echo -n "Stopping $DESC: "
- start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
- --exec $DAEMON
- echo "$NAME."
- rm /var/run/$NAME.pid
- ;;
- #reload)
- #
- # If the daemon can reload its config files on the fly
- # for example by sending it SIGHUP, do it here.
- #
- # If the daemon responds to changes in its config file
- # directly anyway, make this a do-nothing entry.
- #
- # echo "Reloading $DESC configuration files."
- # start-stop-daemon --stop --signal 1 --quiet --pidfile \
- # /var/run/$NAME.pid --exec $DAEMON
- #;;
- restart|force-reload)
- #
- # If the "reload" option is implemented, move the "force-reload"
- # option to the "reload" entry above. If not, "force-reload" is
- # just the same as "restart".
- #
- $0 stop
- sleep 1
- $0 start
- ;;
- *)
- N=/etc/init.d/$NAME
- # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
- echo "Usage: $N {start|stop|restart|force-reload}" >&2
- exit 1
- ;;
-esac
-
-exit 0
foreach my $table (qw(
part_export_option
- payment_gateway
payment_gateway_option
+ payment_gateway
agent_payment_gateway
- queue
queue_arg
+ queue
cust_pay_batch
)) {
print "DELETE FROM $table;\n";
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use Frontier::Client;
+use Data::Dumper;
+
+my $uri = new URI 'http://localhost:8008/';
+
+my $server = new Frontier::Client ( 'url' => $uri );
+
+my $result = $server->call(
+ 'FS.API.customer_info',
+ 'secret' => 'sharingiscaring',
+ 'custnum' => 181318,
+);
+
+#die $result->{'error'} if $result->{'error'};
+
+print Dumper($result);
+
+1;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use Frontier::Client;
+use Data::Dumper;
+
+my $uri = new URI 'http://localhost:8008/';
+
+my $server = new Frontier::Client ( 'url' => $uri );
+
+my $result = $server->call(
+ 'FS.API.insert_credit_phonenum',
+ 'secret' => 'sharingiscaring',
+ 'phonenum' => '3125550001',
+ 'amount' => '54.32',
+
+ #optional
+ '_date' => 1397977200, #UNIX timestamp
+);
+
+#die $result->{'error'} if $result->{'error'};
+
+print Dumper($result);
+
+1;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use Frontier::Client;
+use Data::Dumper;
+
+my $uri = new URI 'http://localhost:8008/';
+
+my $server = new Frontier::Client ( 'url' => $uri );
+
+my $result = $server->call(
+ 'FS.API.insert_payment',
+ 'secret' => 'sharingiscaring',
+ 'custnum' => 181318,
+ 'payby' => 'CASH',
+ 'paid' => '54.32',
+
+ #optional
+ '_date' => 1397977200, #UNIX timestamp
+);
+
+#die $result->{'error'} if $result->{'error'};
+
+print Dumper($result);
+
+1;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use Frontier::Client;
+use Data::Dumper;
+
+my $uri = new URI 'http://localhost:8008/';
+
+my $server = new Frontier::Client ( 'url' => $uri );
+
+my $result = $server->call(
+ 'FS.API.insert_payment_phonenum',
+ 'secret' => 'sharingiscaring',
+ 'phonenum' => '3125550001',
+ 'payby' => 'CASH',
+ 'paid' => '54.32',
+
+ #optional
+ '_date' => 1397977200, #UNIX timestamp
+);
+
+#die $result->{'error'} if $result->{'error'};
+
+print Dumper($result);
+
+1;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use Frontier::Client;
+use Data::Dumper;
+
+my $uri = new URI 'http://localhost:8008/';
+
+my $server = new Frontier::Client ( 'url' => $uri );
+
+my $result = $server->call(
+ 'FS.API.insert_refund_phonenum',
+ 'secret' => 'sharingiscaring',
+ 'phonenum' => '3125550001',
+ 'payby' => 'CASH',
+ 'refund' => '54.32',
+
+ #optional
+ '_date' => 1397977200, #UNIX timestamp
+);
+
+#die $result->{'error'} if $result->{'error'};
+
+print Dumper($result);
+
+1;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use Frontier::Client;
+use Data::Dumper;
+
+my $uri = new URI 'http://localhost:8008/';
+
+my $server = new Frontier::Client ( 'url' => $uri );
+
+my $result = $server->call(
+ 'FS.API.location_info',
+ 'secret' => 'sharingiscaring',
+ 'custnum' => 181318,
+);
+
+#die $result->{'error'} if $result->{'error'};
+
+print Dumper($result);
+
+1;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use Frontier::Client;
+use Data::Dumper;
+
+my( $username, $password ) = ( @ARGV );
+
+my $uri = new URI 'http://localhost:8008/';
+
+my $server = new Frontier::Client ( 'url' => $uri );
+
+my $result = $server->call('FS.API.new_customer',
+ #API shared secret
+ 'secret' => 'sharingiscaring',
+
+ #customer informaiton
+ 'agentnum' => 1,
+ 'refnum' => 1, #advertising source
+ 'agent_custid' => '', #'323',
+ 'referral_custnum' => 1,
+
+ 'first' => 'Tofu',
+ 'last' => 'Beast',
+ 'company' => 'Bank of Soymerica',
+
+ #address
+ 'address1' => '1234 Soybean Ln.',
+ 'city' => 'Tofutown',
+ 'county' => '',
+ 'state' => 'CA',
+ 'zip' => '54321',
+ 'country' => 'US',
+ 'latitude' => '',
+ 'longitude' => '',
+ 'geocode' => '',
+ 'censustract' => '',
+ 'censusyear' => '',
+
+ #phones
+ 'daytime' => '555 444 3211',
+ 'night' => '',
+ 'fax' => '',
+ 'mobile' => '123 466 3332',
+
+ #invoicing info
+ 'invoicing_list' => 'tofu@example.com', #comma-separated email addresses
+ 'postal_invoicing' => 1,
+
+ #billing information
+ 'payby' => 'CARD', # DCRD, CHEK, DCHK, BILL, etc.
+ 'payinfo' => '4111111111111111',#card number / acctno@routing / PO#
+ 'paydate' => '11/2019', #card expiration
+ 'paycvv' => '123', #card CVV/security code
+ 'payname' => 'Thomas Beast', #"Exact name on card" if different
+);
+
+die $result->{'error'} if $result->{'error'};
+
+my $custnum = $result->{'custnum'};
+warn "added new customer w/custnum $custnum\n";
+
+1;
my $server = new Frontier::Client ( 'url' => $uri );
-my $result = $server->call('phonenum_balance', 'phonenum' => '9567566022', );
+my $result = $server->call('FS.ClientAPI_XMLRPC.phonenum_balance', 'phonenum' => '9567566022', );
#die $result->{'error'} if $result->{'error'};
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Frontier::Client;
-use Data::Dumper;
-
-my $server = new Frontier::Client (
- url => 'http://user:pass@freesidehost/misc/xmlrpc.cgi',
-);
-
-#my $method = 'cust_main.smart_search';
-#my @args = (search => '1');
-
-my $method = 'Record.qsearch';
-my @args = (cust_main => { });
-
-my $result = $server->call($method, @args);
-
-if (ref($result) eq 'ARRAY') {
- print "Result:\n";
- print Dumper(@$result);
-}
-
<& elements/browse.html,
- 'title' => 'Fee definitions',
- 'name_singular' => 'fee definition',
- 'query' => $query,
- 'count_query' => $count_query,
- 'header' => [ '#',
+ title => 'Fee definitions',
+ name_singular => 'fee definition',
+ query => $query,
+ count_query => $count_query,
+ header => [ '#',
'Description',
'Comment',
'Class',
'Amount',
'Tax status',
],
- 'fields' => [ 'feepart',
+ fields => [ 'feepart',
'itemdesc',
'comment',
'classname',
$link,
],
align => 'cllccc',
+ menubar => \@menubar,
&>
<%init>
my $curuser = $FS::CurrentUser::CurrentUser;
};
my $link = [ $p.'edit/part_fee.html?', 'feepart' ];
+
+my @menubar = ( 'Add a new fee definition',
+ $p.'edit/part_fee.html' );
</%init>
my @deleteable = qw( invoice_latexreturnaddress invoice_htmlreturnaddress );
my %deleteable = map { $_ => 1 } @deleteable;
-my @sections = qw(required billing invoicing notification UI self-service ticketing network_monitoring username password session shell BIND telephony );
-push @sections, '', 'deprecated';
+my @sections = qw(
+ required billing invoicing notification UI API self-service ticketing
+ network_monitoring username password session shell BIND telephony
+), '', 'deprecated';
my %section_items = ();
foreach my $section (@sections) {
'select' => 'cust_bill_pkg.*',
'table' => 'cust_bill_pkg',
'addl_from' => 'LEFT JOIN cust_bill USING (invnum)',
- 'extra_sql' => "WHERE custnum = $custnum AND pkgnum != 0",
+ 'extra_sql' => "WHERE custnum = $custnum ".
+ "AND (pkgnum != 0 or feepart IS NOT NULL)",
'order_by' => 'ORDER BY invnum ASC, billpkgnum ASC',
});
'credit_weight' => 'Credit weight',
'agentnum' => 'Agent',
'amount' => 'Flat fee amount',
- 'percent' => 'Percentage of invoice amount',
'basis' => 'Based on',
'setuprecur' => 'Report this fee as',
'minimum' => 'Minimum fee',
my (@locale_fields, %locale_labels);
foreach (@locales) {
push @locale_fields,
- { field => 'feepartmsgnum'. $n, type => 'hidden' },
- { field => 'feepartmsgnum'. $n. '_locale', type => 'hidden' },
+ { field => 'feepartmsgnum'. $n, type => 'hidden' },
+ { field => 'feepartmsgnum'. $n. '_locale', type => 'hidden', value => $_ },
{ field => 'feepartmsgnum'. $n. '_itemdesc', type => 'text', size => 40 },
;
$locale_labels{ 'feepartmsgnum'.$n.'_itemdesc' } =
$n++;
}
+$n = 0;
+my %layer_fields = (
+ 'charged' => [
+ 'percent' => { label => 'Fraction of invoice total', type => 'percentage', },
+ ],
+ 'owed' => [
+ 'percent' => { label => 'Fraction of balance', type => 'percentage', },
+ ],
+ 'usage' => [
+ 'usage' => { type => 'part_fee_usage' }
+ ],
+);
+
my @fields = (
{ field => 'itemdesc', type => 'text', size => 40, },
{ type => 'justtitle', value => 'Fee calculation' },
{ field => 'amount', type => 'money', },
- { field => 'percent', type => 'percentage', },
{ field => 'basis',
- type => 'select',
- options => [ 'charged', 'owed' ],
- labels => { 'charged' => 'amount charged',
- 'owed' => 'balance due', },
+ type => 'selectlayers',
+ options => [ 'charged', 'owed', 'usage' ],
+ labels => { 'charged' => 'amount charged',
+ 'owed' => 'balance due',
+ 'usage' => 'usage charges' },
+ layer_fields => \%layer_fields,
+ layer_values_callback => sub {
+ my ($cgi, $obj) = @_;
+ {
+ 'charged' => { percent => $obj->percent },
+ 'owed' => { percent => $obj->percent },
+ 'usage' => { usage => [ $obj->part_fee_usage ] },
+ }
+ },
},
-
{ field => 'minimum', type => 'money', },
{ field => 'maximum', type => 'money', },
{ field => 'limit_credit',
<& elements/process.html,
- 'debug' => 1,
+ #'debug' => 1,
'table' => 'part_fee',
'agent_virt' => 1,
'agent_null_right' => 'Edit global fee definitions',
'viewall_dir' => 'browse',
- 'process_o2m' => {
- 'table' => 'part_fee_msgcat',
- 'fields' => [ 'locale', 'itemdesc' ],
- },
+ 'process_o2m' => [
+ {
+ 'table' => 'part_fee_msgcat',
+ 'fields' => [ 'locale', 'itemdesc' ],
+ },
+ {
+ 'table' => 'part_fee_usage',
+ 'fields' => [ 'classnum',
+ 'amount',
+ 'percent'
+ ],
+ },
+ ],
&>
<%init>
% if ( $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices') ) {
-<A HREF="<%$p%>misc/email-customers.html?table=<%$table%>&<%$query%>"><%$label%></A>
+<A HREF="<%$p%>misc/email-customers.html?table=<%$table%>&agent_virt_agentnum=<%$agent_virt_agentnum%>&<%$query%>"><%$label%></A>
% }
<%init>
my %opt = @_;
my $table = $opt{'table'};
+my $agent_virt_agentnum = $opt{'agent_virt_agentnum'};
my $search_hash = $opt{'search_hash'};
die "'table' required" if !$table;
die "'search_hash' required" if !$search_hash;
--- /dev/null
+% my $n = 0;
+% foreach my $class (@classes) {
+% my $pre = "feepartusagenum$n";
+% my $x = $part_fee_usage{$class->classnum} || FS::part_fee_usage->new({});
+<tr>
+ <td align="right">
+ <input type="hidden" name="<%$pre%>" value="<% $x->partfeeusagenum %>">
+ <input type="hidden" name="<%$pre%>_classnum" value="<% $class->classnum %>">
+ <% $class->classname %>:</td>
+ <td>
+ <%$money_char%><input size=4 name="<%$pre%>_amount" \
+ value="<% sprintf("%.2f", $x->amount) %>">
+ </td>
+ <td>per call<b> + </b></td>
+ <td>
+ <input size=4 name="<%$pre%>_percent" \
+ value="<% sprintf("%.1f", $x->percent) %>">%
+ </td>
+</tr>
+% $n++;
+% }
+<%init>
+my %opt = @_;
+my $value = $opt{'curr_value'} || $opt{'value'};
+# values is an arrayref of part_fee_usage objects
+my %part_fee_usage = map { $_->classnum => $_ } @$value;
+my @classes = qsearch('usage_class', { disabled => '' });
+my $money_char = FS::Conf->new->config('money_char') || '$';
+</%init>
my %hash = (
'show_month_abbr' => 1,
'start_year' => '1999',
- 'end_year' => '2014',
+ 'end_year' => '2016',
@_,
);
</%init>
my $default_start_date = $conf->exists('order_pkg-no_start_date')
? ''
- : $cust_main->next_bill_date;
+ : $cust_main ? $cust_main->next_bill_date
+ : '';
#num_billing_pkgs may be slightly better (will allow you to fill in a start
# date in the weird edge case where you're using sync_next_bill and
<& /elements/tr-td-label.html, 'label' => 'From:' &>
<TD><& /elements/input-text.html,
'field' => 'from_name',
- 'value' => $conf->config('company_name'), #?
+ 'value' => $conf->config('company_name', $agent_virt_agentnum), #?
'size' => 20,
&> <\
<& /elements/input-text.html,
'field' => 'from_addr',
'type' => 'email', # HTML5, woot
- 'value' => $conf->config('invoice_from'),
+ 'value' => $conf->config('invoice_from', $agent_virt_agentnum),
'size' => 20,
&>></TD>
unless $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices');
my $conf = FS::Conf->new;
+
my $table = $cgi->param('table') or die "'table' required";
+my $agent_virt_agentnum = $cgi->param('agent_virt_agentnum') || '';
+
my %search;
if ( $cgi->param('search') ) {
%search = %{ thaw(decode_base64($cgi->param('search'))) };
my $taxlisthash = {};
foreach my $cust_bill_pkg (values %cust_bill_pkg) {
- my $part_pkg = $cust_bill_pkg->part_pkg;
- $cust_main->_handle_taxes( $part_pkg,
- $taxlisthash,
- $cust_bill_pkg,
- $cust_bill_pkg->cust_pkg,
- $cust_bill_pkg->cust_bill->_date,
- $cust_bill_pkg->cust_pkg->pkgpart,
- );
+ $cust_main->_handle_taxes( $taxlisthash, $cust_bill_pkg );
}
my $listref_or_error =
$cust_main->calculate_taxes( [ values %cust_bill_pkg ], $taxlisthash, [ values %cust_bill_pkg ]->[0]->cust_bill->_date );
push @cust_bill_pkg, $cust_bill_pkg;
- my $part_pkg = $cust_bill_pkg->part_pkg;
- $cust_main->_handle_taxes( $part_pkg,
- $taxlisthash,
- $cust_bill_pkg,
- $cust_bill_pkg->cust_pkg,
- $cust_bill_pkg->cust_bill->_date,
- $cust_bill_pkg->cust_pkg->pkgpart,
- );
-
+ $cust_main->_handle_taxes( $taxlisthash, $cust_bill_pkg );
}
if ( @cust_bill_pkg ) {
foreach my $taxline ( @$listref_or_error ) {
my $amount = $taxline->setup;
my $desc = $taxline->desc;
- foreach my $location ( @{$taxline->cust_bill_pkg_tax_location}, @{$taxline->cust_bill_pkg_tax_rate_location} ) {
+ foreach my $location (
+ @{$taxline->get('cust_bill_pkg_tax_location')},
+ @{$taxline->get('cust_bill_pkg_tax_rate_location')} )
+ {
my $taxlocnum = $location->locationnum || '';
my $taxratelocnum = $location->taxratelocationnum || '';
$location->cust_bill_pkg_desc($taxline->desc); #ugh @ that kludge
+++ /dev/null
-<% $response_xml %>\
-<%init>
-
-my $request_xml = $cgi->param('POSTDATA');
-
-#warn $request_xml;
-
-my $fsxmlrpc = new FS::XMLRPC;
-my ($error, $response_xml) = $fsxmlrpc->serve($request_xml);
-
-#warn $error;
-
-http_header('Content-Type' => 'text/xml',
- 'Content-Length' => length($response_xml));
-
-</%init>
<& "477part${part}.html",
'tech_code' => $tech,
'url' => $url,
- 'type' => $type
+ 'type' => $type,
+ 'date' => $date,
&>
% if ( $type eq 'xml' ) {
</<% 'Part_IA_'. chr(65 + $tech) %>>
% }
% }
-% } else {
+% } else { # not part IA
% if ( $type eq 'xml' ) {
<<% 'Part_'. $part %>>
% }
% my $url = &{$url_mangler}($part);
-<& "477part${part}.html", 'url' => $url &>
+<& "477part${part}.html",
+ 'url' => $url,
+ 'date' => $date,
+&>
% if ( $type eq 'xml' ) {
</<% 'Part_'. $part %>>
% }
die "access denied"
unless $curuser->access_right('List packages');
+my $date = $cgi->param('date') ? parse_datetime($cgi->param('date'))
+ : time;
+
my $state = uc($cgi->param('state'));
$state =~ /^[A-Z]{2}$/ or die "illegal state: $state";
for ( qw(agentnum state) ) {
$search_hash{$_} = $cgi->param($_) if $cgi->param($_);
}
-$search_hash{'status'} = 'active';
$search_hash{'country'} = 'US';
$search_hash{'classnum'} = [ $cgi->param('classnum') ];
# whether to show residential percentages in each cell of the matrix
my $percentages = ($technology eq 'Terrestrial Mobile Wireless');
+# as of date
+# FCC 477 instructions: "Only count connections that are in service."
+# So we count packages that were in active status as of the specified date,
+# not over any sort of range.
+$search_hash{'active'} = [ $opt{date}, $opt{date} ];
+warn Dumper \%search_hash;
+
my $query = FS::cust_pkg->search(\%search_hash);
my $count_query = $query->{'count_query'};
</TABLE>
% } #XML/HTML
<%init>
-
my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
unless $curuser->access_right('List packages');
+my %opt = @_;
my %search_hash = ();
$search_hash{'agentnum'} = $cgi->param('agentnum');
$search_hash{'state'} = $cgi->param('state');
$search_hash{'classnum'} = [ $cgi->param('classnum') ];
-$search_hash{'status'} = 'active';
+$search_hash{'active'} = [ $opt{date}, $opt{date} ];
my @row_option;
foreach ($cgi->param('part2a_row_option')) {
push @row_option, (/^\d+$/ ? $_ : undef);
}
-my $is_residential = "AND COALESCE(cust_main.company, '') = ''";
+my $is_residential = " AND COALESCE(cust_main.company, '') = ''";
my $has_report_option = sub {
map {
defined($row_option[$_]) ?
die "access denied"
unless $curuser->access_right('List packages');
+my %opt = @_;
my %search_hash = ();
$search_hash{'agentnum'} = $cgi->param('agentnum');
$search_hash{'state'} = $cgi->param('state');
$search_hash{'classnum'} = [ $cgi->param('classnum') ];
-$search_hash{'status'} = 'active';
+$search_hash{'active'} = [ $opt{date}, $opt{date} ];
my @row_option;
foreach ($cgi->param('part2b_row_option')) {
push @row_option, (/^\d+$/ ? $_ : undef);
}
-my $is_residential = "AND COALESCE(cust_main.company, '') = ''";
+my $is_residential = " AND COALESCE(cust_main.company, '') = ''";
my $has_report_option = sub {
map {
defined($row_option[$_]) ?
- "AND EXISTS(
+ " AND EXISTS(
SELECT 1 FROM part_pkg_option
WHERE part_pkg_option.pkgpart = part_pkg.pkgpart
AND optionname = 'report_option_" . $row_option[$_]."'
AND optionvalue = '1'
- )" : 'AND FALSE'
+ )" : ' AND FALSE'
} @_
};
my @sql_query = ();
my @count_query = ();
-for ( qw(agentnum magic state) ) {
+for ( qw(agentnum state) ) {
$search_hash{$_} = $cgi->param($_) if $cgi->param($_);
}
$search_hash{'country'} = 'US';
$search_hash{'classnum'} = [ $cgi->param('classnum') ];
$search_hash{report_option} = $cgi->param('part5_report_option')
if $cgi->param('part5_report_option');
+$search_hash{'active'} = [ $opt{date}, $opt{date} ];
my $sql_query = FS::cust_pkg->search( { %search_hash,
'fcc_line' => 1,
my %search_hash = ();
my @sql_query = ();
-for ( qw(agentnum magic state) ) {
+for ( qw(agentnum state) ) {
$search_hash{$_} = $cgi->param($_) if $cgi->param($_);
}
+
+$search_hash{'active'} = [ $opt{date}, $opt{date} ];
$search_hash{'country'} = 'US';
$search_hash{'classnum'} = [ $cgi->param('classnum') ]
if grep { $_ eq 'classnum' } $cgi->param;
($report_option ? ( 'report_option' => $report_option ) : () ),
}
);
+warn Dumper($sql_query) if $rowcount==1 and $columncount==3;
my $extracolumns = "$rowcount AS upload, $columncount AS download, $tech_code as technology_code";
my $percent = "CASE WHEN count(*) > 0 THEN 100-100*cast(count(cust_main.company) as numeric)/cast(count(*) as numeric) ELSE cast(0 as numeric) END AS residential";
$sql_query->{select} = "count(*) AS quantity, $extracolumns, cust_location.censustract, $percent";
- $sql_query->{order_by} =~ /^(.*)(ORDER BY pkgnum)(.*)$/s
- or die "couldn't parse order_by";
- $sql_query->{order_by} = "$1 GROUP BY cust_location.censustract $3";
+ $sql_query->{order_by} = " GROUP BY cust_location.censustract ";
push @sql_query, $sql_query;
}
$columncount++;
- use_override: Apply "classnum" and "taxclass" filtering based on the
override (bundle) pkgpart, rather than always using the true pkgpart.
-- nottax: Limit to items that are not taxes (pkgnum > 0).
+- nottax: Limit to items that are not taxes (pkgnum > 0 or feepart > 0).
-- istax: Limit to items that are taxes (pkgnum == 0).
+- istax: Limit to items that are taxes (pkgnum == 0 and feepart = null).
- taxnum: Limit to items whose tax definition matches this taxnum.
With "nottax" that means items that are subject to that tax;
# we want the package and its definition if available
my $join_pkg =
' LEFT JOIN cust_pkg USING (pkgnum)
- LEFT JOIN part_pkg USING (pkgpart)';
+ LEFT JOIN part_pkg USING (pkgpart)
+ LEFT JOIN part_fee USING (feepart)';
my $part_pkg = 'part_pkg';
# "Separate sub-packages from parents"
$part_pkg = 'override';
}
push @select, "$part_pkg.pkgpart", "$part_pkg.pkg";
-push @select, "$part_pkg.taxclass" if $conf->exists('enable_taxclasses');
+push @select, "COALESCE($part_pkg.taxclass, part_fee.taxclass) AS taxclass"
+ if $conf->exists('enable_taxclasses');
# the non-tax case
if ( $cgi->param('nottax') ) {
- push @where, 'cust_bill_pkg.pkgnum > 0';
+ push @select, "part_fee.itemdesc";
+
+ push @where,
+ '(cust_bill_pkg.pkgnum > 0 OR cust_bill_pkg.feepart IS NOT NULL)';
my @tax_where; # will go into a subquery
my @exempt_where; # will also go into a subquery
# N: classnum
if ( grep { $_ eq 'classnum' } $cgi->param ) {
my @classnums = grep /^\d*$/, $cgi->param('classnum');
- push @where, "COALESCE($part_pkg.classnum, 0) IN ( ".
+ push @where, "COALESCE(part_fee.classnum, $part_pkg.classnum, 0) IN ( ".
join(',', @classnums ).
' )'
if @classnums;
# effective taxclass, not the real one
push @tax_where, 'cust_main_county.taxclass IS NULL'
} elsif ( $cgi->param('taxclass') ) {
- push @tax_where, "$part_pkg.taxclass IN (" .
+ push @tax_where, "COALESCE(part_fee.taxclass, $part_pkg.taxclass) IN (" .
join(', ', map {dbh->quote($_)} $cgi->param('taxclass') ).
')';
}
'paid' => ($cgi->param('paid') ? 1 : 0),
'classnum' => scalar($cgi->param('classnum'))
);
- $join_pkg .= " JOIN sales_pkg_class ON ( COALESCE(sales_pkg_class.classnum, 0) = COALESCE( part_pkg.classnum, 0) )";
+ $join_pkg .= " JOIN sales_pkg_class ON ( COALESCE(sales_pkg_class.classnum, 0) = COALESCE( part_fee.classnum, part_pkg.classnum, 0) )";
my $extra_sql = $subsearch->{extra_sql};
$extra_sql =~ s/^WHERE//;
'classname',
sub { # report_option
my $cust_bill_pkg = shift;
- my $pkgpart = $cust_bill_pkg->pkgpart_override
- || $cust_bill_pkg->cust_pkg->pkgpart;
+ my $pkgpart = $cust_bill_pkg->pkgpart_override;
+ unless ( $pkgpart ) {
+ my $cust_pkg = $cust_bill_pkg->cust_pkg or return '';
+ $pkgpart = $cust_pkg->pkgpart;
+ }
if ( !exists($report_classes{$pkgpart}) ) {
my $part_pkg = FS::part_pkg->by_key($pkgpart);
my %opts = $part_pkg->options;
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
my @where = ( $agentnums_sql,
- 'cust_bill_pkg.pkgnum != 0', # exclude taxes
+ # exclude taxes
+ '(cust_bill_pkg.pkgnum != 0 OR '.
+ 'cust_bill_pkg.feepart IS NOT NULL)',
"cust_bill._date >= $beginning",
"cust_bill._date <= $ending",
);
if ( $use_override ) {
push @where, "(
- part_pkg.classnum $comparison AND pkgpart_override IS NULL OR
- override.classnum $comparison AND pkgpart_override IS NOT NULL
+ (part_pkg.classnum $comparison AND pkgpart_override IS NULL) OR
+ (override.classnum $comparison AND pkgpart_override IS NOT NULL) OR
+ (part_fee.classnum $comparison AND feepart IS NOT NULL)
)";
} else {
- push @where, "part_pkg.classnum $comparison";
+ push @where,
+ "(part_pkg.classnum $comparison) OR (part_fee.classnum $comparison)";
}
}
LEFT JOIN part_pkg USING ( pkgpart )
LEFT JOIN part_pkg AS override
ON pkgpart_override = override.pkgpart
+ LEFT JOIN part_fee USING ( feepart )
LEFT JOIN pkg_class ON '; #...
if ( $use_override ) {
# join to whichever pkgpart is appropriate
$join_pkg .= '
( pkgpart_override IS NULL AND part_pkg.classnum = pkg_class.classnum )
- OR ( pkgpart_override IS NOT NULL AND override.classnum = pkg_class.classnum )';
+ OR ( pkgpart_override IS NOT NULL AND override.classnum = pkg_class.classnum )
+ OR ( feepart IS NOT NULL AND part_fee.classnum = pkg_class.classnum )';
} else {
- $join_pkg .= 'part_pkg.classnum = pkg_class.classnum';
+ $join_pkg .= '
+ ( part_pkg.classnum = pkg_class.classnum )
+ OR ( part_fee.classnum = pkg_class.classnum )';
}
my $where = ' WHERE '. join(' AND ', @where);
FROM cust_bill_pay JOIN cust_bill_pay_pkg USING (billpaynum)
WHERE cust_bill_pay_pkg.billpkgnum = cust_bill_pkg.billpkgnum";
-push @select, 'part_pkg.pkg',
+push @select, 'COALESCE(part_pkg.pkg, part_fee.itemdesc) AS pkg',
'part_pkg.freq',
'cust_main.custnum',
'cust_main.first',
'count_query' => $count_query,
'header' => [
'Date',
- 'Template',
+ 'Type',
'Destination',
'Status',
'', #error
my $date = $_[0]->_date;
$date ? time2str('%Y-%m-%d %T',$_[0]->_date) : ''
},
- 'msgname',
+ sub {
+ ucfirst($_[0]->msgtype) || $_[0]->msgname
+ },
sub {
join('<BR>', split(/,\s*/, $_[0]->env_to) )
},
'',
'',
],
- 'color' => [ ('') x 3,
- $statuscolor,
- $statuscolor,
+ 'color' => [ '',
+ $typecolor,
+ '',
+ $statuscolor,
+ $statuscolor,
],
'html_init' => $html_init,
'really_disable_download' => 1,
if ( $cgi->param('status') =~ /^(\w+)$/ ) {
push @where, "status = '$1'";
}
+if ( $cgi->param('msgtype') =~ /^(\w+)$/ ) {
+ push @where, "msgtype = '$1'";
+}
my ($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, '');
push @where, "(_date >= $beginning AND _date <= $ending)";
include('/elements/popup_link_onclick.html',
'action' => $p. 'view/cust_msg.html?' . $custmsgnum,
'actionlabel' => 'Message detail',
- 'width' => 600,
- 'height' => 500,
+ 'width' => 680,
+ 'height' => 550,
);
};
my %color = (
'failed' => 'FF0000',
'sent' => '',
+
+ 'invoice' => '00CC00',
+ 'receipt' => '0000CC',
+ 'admin' => 'CC0000',
+ '' => '000000',
);
my $statuscolor = sub { $color{$_[0]->status} };
+my $typecolor = sub { $color{$_[0]->msgtype} };
my $html_init = qq!<FORM ACTION="$p/search/cust_msg.html" METHOD="GET">
<TABLE cellspacing="10">!.
'failed' => 'failed',
'sent' => 'sent', },
) .
+'</TD><TD> Type '.
+include('/elements/select.html',
+ 'field' => 'msgtype',
+ 'curr_value' => $cgi->param('msgtype') || '',
+ 'options' => [ '', 'invoice', 'receipt', 'admin' ],
+ 'labels' => { '' => '(any)',
+ 'invoice' => 'Invoices',
+ 'receipt' => 'Receipts',
+ 'admin' => 'Admin notices',
+ },
+) .
'</TD>
<TD><INPUT type="submit" value="Search"></TD></TR>
</TABLE></FORM><BR>
% $cstyle = qq(STYLE="$cstyle")
% if $cstyle;
- <TD CLASS="<% $class %>" BGCOLOR="<% $bgcolor %>" <% $align %> <% $cstyle %>><% $font %><% $a %><% $s %><% $field %><% $es %><% $a ? '</A>' : '' %><% $font ? '</FONT>' : '' %></TD>
+ <TD CLASS="<% $class %>" BGCOLOR="<% $bgcolor %>" <% $align %> <% $cstyle %>><% $a %><% $font %><% $s %><% $field %><% $es %><% $font ? '</FONT>' : '' %><% $a ? '</A>' : '' %></TD>
% }
%
'records' => \@states,
&>
+ <& /elements/tr-input-date-field.html, {
+ 'label' => 'As of date',
+ 'name' => 'date',
+ 'value' => '',
+ 'format' => '%m/%d/%Y'
+ } &>
+
<% include( '/elements/tr-select-pkg_class.html',
'multiple' => 1,
'empty_label' => '(empty class)',
$where .= ' AND cust_main.agentnum = '. $agent->agentnum;
}
-my $nottax = 'cust_bill_pkg.pkgnum != 0';
+my $nottax =
+ '(cust_bill_pkg.pkgnum != 0 OR cust_bill_pkg.feepart IS NOT NULL)';
# one query for each column of the report
# plus separate queries for the totals row
my %query = ( 'table' => 'sales' );
my $count_query = "SELECT COUNT(*) FROM sales";
-my $salesnum;
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $query{hashref}->{agentnum} = $1;
+ $count_query .= " WHERE agentnum = $1";
+}
+
+my $salesnum = '';
if ( $cgi->param('salesnum') =~ /^(\d+)$/ ) {
$salesnum = $1;
} else {
% # and finally, display the thing
% ###
%
-% foreach my $part_export (
-% #grep $_->can('usage_sessions'), qsearch( 'part_export' )
-% qsearch( 'part_export', { 'exporttype' => 'sqlradius' } ),
-% qsearch( 'part_export', { 'exporttype' => 'sqlradius_withdomain' } )
-% ) {
+% foreach my $part_export ( @part_export ) {
+%
% %user2svc = ();
%
% my $efields = tie my %efields, 'Tie::IxHash', %fields;
% },
% );
% }
-%
-%
- <% $part_export->exporttype %> to <% $part_export->machine %><BR>
+ <FONT CLASS="fsinnerbox-title">
+ <% $part_export->exportname || $part_export->exporttype |h %>
+ <% $part_export->machine ? ' to '. $part_export->machine : '' |h %>
+ </FONT><BR>
+
<% include( '/elements/table-grid.html' ) %>
% my $bgcolor1 = '#eeeeee';
% my $bgcolor2 = '#ffffff';
$cgi_svc = qsearchs( 'svc_acct', { 'username' => $1 } );
}
+my @part_export = ();
+if ( $cgi_svc ) {
+ my $part_svc = $cgi_svc->cust_svc->part_svc;
+ @part_export = (
+ $part_svc->part_export('sqlradius'),
+ $part_svc->part_export('sqlradius_withdomain'),
+ $part_svc->part_export('broadband_sqlradius'),
+ );
+} else {
+ @part_export = (
+ #grep $_->can('usage_sessions'), qsearch( 'part_export' )
+ qsearch( 'part_export', { 'exporttype' => 'sqlradius' } ),
+ qsearch( 'part_export', { 'exporttype' => 'sqlradius_withdomain' } ),
+ qsearch( 'part_export', { 'exporttype' => 'broadband_sqlradius' } ),
+ );
+}
+
my $ip = '';
if ( $cgi->param('ip') =~ /^((\d+\.){3}\d+)$/ ) {
$ip = $1;
%
% my $email_link = ($cust_main->invoicing_list_emailonly) &&
% include('/elements/email-link.html',
-% 'table' => 'cust_main',
-% 'search_hash' => { 'custnum' => $custnum },
-% 'label' => 'Email a notice to this customer',
+% 'table' => 'cust_main',
+% 'search_hash' => { 'custnum' => $custnum },
+% 'agent_virt_agentnum' => $cust_main->agentnum,
+% 'label' => 'Email a notice to this customer',
% );
% if ( $email_link and $br ) {
|
### END INIT INFO
QUEUED_USER=%%%QUEUED_USER%%%
+API_USER=%%%API_USER%%%
SELFSERVICE_USER=%%%SELFSERVICE_USER%%%
SELFSERVICE_MACHINES="%%%SELFSERVICE_MACHINES%%%"
case "$1" in
start)
# Start daemons.
+
+ for MACHINE in $SELFSERVICE_MACHINES; do
+ echo -n "Starting freeside-selfservice-server to $MACHINE: "
+ freeside-selfservice-server $SELFSERVICE_USER $MACHINE
+ echo "done."
+ done
+
+ echo -n "Starting freeside-selfservice-xmlrpcd: "
+ freeside-selfservice-xmlrpcd $SELFSERVICE_USER
+ echo "done."
+
+ echo -n "Starting freeside-xmlrpcd: "
+ freeside-xmlrpcd $API_USER
+ echo "done."
+
+# echo -n "Starting freeside-jsonrpcd: "
+# freeside-jsonrpcd $API_USER
+# echo "done."
+
echo -n "Starting freeside-queued: "
#perl -MDBIx::Profile /usr/local/bin/freeside-queued $QUEUED_USER
freeside-queued $QUEUED_USER
freeside-cdrrated $QUEUED_USER
echo "done."
- for MACHINE in $SELFSERVICE_MACHINES; do
- echo -n "Starting freeside-selfservice-server to $MACHINE: "
- freeside-selfservice-server $SELFSERVICE_USER $MACHINE
- echo "done."
- done
-
- echo -n "Starting freeside-selfservice-xmlrpcd: "
- freeside-selfservice-xmlrpcd $SELFSERVICE_USER
- echo "done."
-
if [ -e /usr/local/bin/torrus ]; then
echo -n "Starting torrus collector: "
/usr/local/bin/torrus collector --tree=main
echo "done."
fi
+ if [ -e /var/run/freeside/torrus-srvderive.pid ]; then
+ echo -n "Stopping freeside-torrus-srvderive: "
+ kill `cat /var/run/freeside/torrus-srvderive.pid`
+ echo "done."
+ fi
+
+ if [ -e /var/run/torrus/collector.main_?.pid ]; then
+ echo -n "Stopping torrus collector: "
+ kill `cat /var/run/torrus/collector.main_?.pid`
+ echo "done."
+ fi
+
+ if [ -e /var/run/freeside/xmlrpcd.pid ]; then
+ echo -n "Stopping freeside-xmlrpcd: "
+ kill `cat /var/run/freeside/xmlrpcd.pid`
+ echo "done."
+ fi
+
+# if [ -e /var/run/freeside/jsonrpcd.pid ]; then
+# echo -n "Stopping freeside-jsonrpcd: "
+# kill `cat /var/run/freeside/jsonrpcd.pid`
+# echo "done."
+# fi
+
if [ -e /var/run/freeside-selfservice-server.$SELFSERVICE_USER.pid ]
then
echo -n "Stopping (old) freeside-selfservice-server: "
echo "done."
fi
- if [ -e /var/run/freeside/torrus-srvderive.pid ]; then
- echo -n "Stopping freeside-torrus-srvderive: "
- kill `cat /var/run/freeside/torrus-srvderive.pid`
- echo "done."
- fi
-
- if [ -e /var/run/torrus/collector.main_?.pid ]; then
- echo -n "Stopping torrus collector: "
- kill `cat /var/run/torrus/collector.main_?.pid`
- echo "done."
- fi
-
;;
restart)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
install: testdeps config-install dirs files-install fixperms instruct
-files-install: libs-install etc-install config-install bin-install sbin-install html-install local-install doc-install font-install po-install
+files-install: libs-install etc-install config-install bin-install sbin-install html-install doc-install font-install po-install
config-install:
@COMMENT_INPLACE_LAYOUT@ $(INSTALL) -m 0755 -o $(BIN_OWNER) -g $(RTGROUP) -d $(DESTDIR)$(CONFIG_FILE_PATH)
@COMMENT_INPLACE_LAYOUT@ $(INSTALL) -o $(BIN_OWNER) -g $(RTGROUP) -m 0755 "bin/$$file" "$(DESTDIR)$(RT_BIN_PATH)/" ; \
@COMMENT_INPLACE_LAYOUT@ done
-local-install:
- -( cd local/html && find . -type d -print ) | while read dir ; do \
- $(INSTALL) -m 0755 -d "$(DESTDIR)$(MASON_LOCAL_HTML_PATH)/$$dir" ; \
- done
- -( cd local/html && find . -type f -print ) | while read file ; do \
- $(INSTALL) -m 0644 "local/html/$$file" "$(DESTDIR)$(MASON_LOCAL_HTML_PATH)/$$file" ; \
- done
- -( cd local/po && find . -type d -print ) | while read dir ; do \
- $(INSTALL) -m 0755 -d "$(DESTDIR)$(LOCAL_LEXICON_PATH)/$$dir" ; \
- done
- -( cd local/po && find . -type f -print ) | while read file ; do \
- $(INSTALL) -m 0644 "local/po/$$file" "$(DESTDIR)$(LOCAL_LEXICON_PATH)/$$file" ; \
- done
- -( cd local/etc && find . -type d -print ) | while read dir ; do \
- $(INSTALL) -m 0755 -d "$(DESTDIR)$(LOCAL_ETC_PATH)/$$dir" ; \
- done
- -( cd local/etc && find . -type f -print ) | while read file ; do \
- $(INSTALL) -m 0644 "etc/$$file" "$(DESTDIR)$(LOCAL_ETC_PATH)/$$file" ; \
- done
regenerate-catalogs:
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
sub edit {
my ($action) = @_;
my (%data, $type, @objects);
- my ($cl, $text, $edit, $input, $output);
+ my ($cl, $text, $edit, $input, $output, $content_type);
use vars qw(%set %add %del);
%set = %add = %del = ();
if (/^-e$/) { $edit = 1 }
elsif (/^-i$/) { $input = 1 }
elsif (/^-o$/) { $output = 1 }
+ elsif (/^-ct$/) { $content_type = shift @ARGV }
elsif (/^-t$/) {
$bad = 1, last unless defined($type = get_type_argument());
}
return 0;
}
+ my @files;
+ @files = @{ vsplit($set{'attachment'}) } if exists $set{'attachment'};
+
my $synerr = 0;
EDIT:
# We'll let the user edit the form before sending it to the server,
# unless we have enough information to submit it non-interactively.
+ if ( $type && $type eq 'ticket' && $text !~ /^Content-Type:/m ) {
+ $text .= "Content-Type: $content_type\n"
+ if $content_type and $content_type ne "text/plain";
+ }
+
if ($edit || (!$input && !$cl)) {
- my $newtext = vi($text);
+ my ($newtext) = vi_form_while(
+ $text,
+ sub {
+ my ($text, $form) = @_;
+ return 1 unless exists $form->[2]{'Attachment'};
+
+ foreach my $f ( @{ vsplit($form->[2]{'Attachment'}) } ) {
+ return (0, "File '$f' doesn't exist") unless -f $f;
+ }
+ @files = @{ vsplit($form->[2]{'Attachment'}) };
+ return 1;
+ },
+ );
+ return $newtext unless $newtext;
# We won't resubmit a bad form unless it was changed.
$text = ($synerr && $newtext eq $text) ? undef : $newtext;
}
+ delete @data{ grep /^attachment_\d+$/, keys %data };
+ my $i = 1;
+ foreach my $file (@files) {
+ $data{"attachment_$i"} = bless([ $file ], "Attachment");
+ $i++;
+ }
+
if ($text) {
my $r = submit("$REST/edit", {content => $text, %data});
if ($r->code == 409) {
# If we submitted a bad form, we'll give the user a chance
# to correct it and resubmit.
if ($edit || (!$input && !$cl)) {
- $text = $r->content;
+ my $content = $r->content . "\n";
+ $content =~ s/^(?!#)/# /mg;
+ $text = $content . $text;
$synerr = 1;
goto EDIT;
}
sub comment {
my ($action) = @_;
- my (%data, $id, @files, @bcc, @cc, $msg, $wtime, $edit);
+ my (%data, $id, @files, @bcc, @cc, $msg, $content_type, $wtime, $edit);
my $bad = 0;
while (@ARGV) {
if (/^-e$/) {
$edit = 1;
}
- elsif (/^-[abcmw]$/) {
+ elsif (/^-(?:[abcmw]|ct)$/) {
unless (@ARGV) {
whine "No argument specified with $_.";
$bad = 1; last;
}
push @files, shift @ARGV;
}
+ elsif (/-ct/) {
+ $content_type = shift @ARGV;
+ }
elsif (/-([bc])/) {
my $a = $_ eq "-b" ? \@bcc : \@cc;
@$a = split /\s*,\s*/, shift @ARGV;
while (<STDIN>) { $msg .= $_ }
}
}
-
elsif (/-w/) { $wtime = shift @ARGV }
}
elsif (!$id && m|^(?:ticket/)?($idlist)$|) {
my $form = [
"",
- [ "Ticket", "Action", "Cc", "Bcc", "Attachment", "TimeWorked", "Text" ],
+ [ "Ticket", "Action", "Cc", "Bcc", "Attachment", "TimeWorked", "Content-Type", "Text" ],
{
Ticket => $id,
Action => $action,
Bcc => [ @bcc ],
Attachment => [ @files ],
TimeWorked => $wtime || '',
+ 'Content-Type' => $content_type || 'text/plain',
Text => $msg || '',
Status => ''
}
my $text = Form::compose([ $form ]);
if ($edit || !$msg) {
- my $error = 0;
- my ($c, $o, $k, $e);
-
- do {
- my $ntext = vi($text);
- return if ($error && $ntext eq $text);
- $text = $ntext;
- $form = Form::parse($text);
- $error = 0;
-
- ($c, $o, $k, $e) = @{ $form->[0] };
- if ($e) {
- $error = 1;
- $c = "# Syntax error.";
- goto NEXT;
- }
- elsif (!@$o) {
- return 0;
- }
- @files = @{ vsplit($k->{Attachment}) };
-
- NEXT:
- $text = Form::compose([[$c, $o, $k, $e]]);
- } while ($error);
+ my ($tmp) = vi_form_while(
+ $text,
+ sub {
+ my ($text, $form) = @_;
+ foreach my $f ( @{ vsplit($form->[2]{'Attachment'}) } ) {
+ return (0, "File '$f' doesn't exist") unless -f $f;
+ }
+ @files = @{ vsplit($form->[2]{'Attachment'}) };
+ return 1;
+ },
+ );
+ return $tmp unless $tmp;
+ $text = $tmp;
}
my $i = 1;
return $passwd;
}
+sub vi_form_while {
+ my $text = shift;
+ my $cb = shift;
+
+ my $error = 0;
+ my ($c, $o, $k, $e);
+ do {
+ my $ntext = vi($text);
+ return undef if ($error && $ntext eq $text);
+
+ $text = $ntext;
+
+ my $form = Form::parse($text);
+ $error = 0;
+ ($c, $o, $k, $e) = @{ $form->[0] };
+ if ( $e ) {
+ $error = 1;
+ $c = "# Syntax error.";
+ goto NEXT;
+ }
+ elsif (!@$o) {
+ return 0;
+ }
+
+ my ($status, $msg) = $cb->( $text, [$c, $o, $k, $e] );
+ unless ( $status ) {
+ $error = 1;
+ $c = "# $msg";
+ }
+
+ NEXT:
+ $text = Form::compose([[$c, $o, $k, $e]]);
+ } while ($error);
+
+ return $text;
+}
+
sub vi {
my ($text) = @_;
my $editor = $ENV{EDITOR} || $ENV{VISUAL} || "vi";
}
push @words, $s;
}
- elsif ( $a =~ /^q{/ ) {
+ elsif ( $a =~ /^q\{/ ) {
my $s = $a;
- while ( $a !~ /}$/ ) {
+ while ( $a !~ /\}$/ ) {
( $a, $b ) =
split /\s*,\s*/, $b, 2;
$s .= ',' . $a;
}
- $s =~ s/^q{/'/;
- $s =~ s/}/'/;
+ $s =~ s/^q\{/'/;
+ $s =~ s/\}/'/;
push @words, $s;
}
else {
-S var=val
Submits the specified variable with the request.
-t type Specifies object type.
+ -ct content-type Specifies content type of message(tickets only).
Examples:
# Interactive (starts $EDITOR with a form).
rt edit ticket/3
rt create -t ticket
+ rt create -t ticket -ct text/html
# Non-interactive.
rt edit ticket/1-3 add cc=foo@example.com set priority=3 due=tomorrow
Options:
-m <text> Specify comment text.
+ -ct <content-type> Specify content-type of comment text.
-a <file> Attach a file to the comment. (May be used more
than once to attach multiple files.)
-c <addrs> A comma-separated list of Cc addresses.
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
sub edit {
my ($action) = @_;
my (%data, $type, @objects);
- my ($cl, $text, $edit, $input, $output);
+ my ($cl, $text, $edit, $input, $output, $content_type);
use vars qw(%set %add %del);
%set = %add = %del = ();
if (/^-e$/) { $edit = 1 }
elsif (/^-i$/) { $input = 1 }
elsif (/^-o$/) { $output = 1 }
+ elsif (/^-ct$/) { $content_type = shift @ARGV }
elsif (/^-t$/) {
$bad = 1, last unless defined($type = get_type_argument());
}
return 0;
}
+ my @files;
+ @files = @{ vsplit($set{'attachment'}) } if exists $set{'attachment'};
+
my $synerr = 0;
EDIT:
# We'll let the user edit the form before sending it to the server,
# unless we have enough information to submit it non-interactively.
+ if ( $type && $type eq 'ticket' && $text !~ /^Content-Type:/m ) {
+ $text .= "Content-Type: $content_type\n"
+ if $content_type and $content_type ne "text/plain";
+ }
+
if ($edit || (!$input && !$cl)) {
- my $newtext = vi($text);
+ my ($newtext) = vi_form_while(
+ $text,
+ sub {
+ my ($text, $form) = @_;
+ return 1 unless exists $form->[2]{'Attachment'};
+
+ foreach my $f ( @{ vsplit($form->[2]{'Attachment'}) } ) {
+ return (0, "File '$f' doesn't exist") unless -f $f;
+ }
+ @files = @{ vsplit($form->[2]{'Attachment'}) };
+ return 1;
+ },
+ );
+ return $newtext unless $newtext;
# We won't resubmit a bad form unless it was changed.
$text = ($synerr && $newtext eq $text) ? undef : $newtext;
}
+ delete @data{ grep /^attachment_\d+$/, keys %data };
+ my $i = 1;
+ foreach my $file (@files) {
+ $data{"attachment_$i"} = bless([ $file ], "Attachment");
+ $i++;
+ }
+
if ($text) {
my $r = submit("$REST/edit", {content => $text, %data});
if ($r->code == 409) {
# If we submitted a bad form, we'll give the user a chance
# to correct it and resubmit.
if ($edit || (!$input && !$cl)) {
- $text = $r->content;
+ my $content = $r->content . "\n";
+ $content =~ s/^(?!#)/# /mg;
+ $text = $content . $text;
$synerr = 1;
goto EDIT;
}
sub comment {
my ($action) = @_;
- my (%data, $id, @files, @bcc, @cc, $msg, $wtime, $edit);
+ my (%data, $id, @files, @bcc, @cc, $msg, $content_type, $wtime, $edit);
my $bad = 0;
while (@ARGV) {
if (/^-e$/) {
$edit = 1;
}
- elsif (/^-[abcmw]$/) {
+ elsif (/^-(?:[abcmw]|ct)$/) {
unless (@ARGV) {
whine "No argument specified with $_.";
$bad = 1; last;
}
push @files, shift @ARGV;
}
+ elsif (/-ct/) {
+ $content_type = shift @ARGV;
+ }
elsif (/-([bc])/) {
my $a = $_ eq "-b" ? \@bcc : \@cc;
@$a = split /\s*,\s*/, shift @ARGV;
while (<STDIN>) { $msg .= $_ }
}
}
-
elsif (/-w/) { $wtime = shift @ARGV }
}
elsif (!$id && m|^(?:ticket/)?($idlist)$|) {
my $form = [
"",
- [ "Ticket", "Action", "Cc", "Bcc", "Attachment", "TimeWorked", "Text" ],
+ [ "Ticket", "Action", "Cc", "Bcc", "Attachment", "TimeWorked", "Content-Type", "Text" ],
{
Ticket => $id,
Action => $action,
Bcc => [ @bcc ],
Attachment => [ @files ],
TimeWorked => $wtime || '',
+ 'Content-Type' => $content_type || 'text/plain',
Text => $msg || '',
Status => ''
}
my $text = Form::compose([ $form ]);
if ($edit || !$msg) {
- my $error = 0;
- my ($c, $o, $k, $e);
-
- do {
- my $ntext = vi($text);
- return if ($error && $ntext eq $text);
- $text = $ntext;
- $form = Form::parse($text);
- $error = 0;
-
- ($c, $o, $k, $e) = @{ $form->[0] };
- if ($e) {
- $error = 1;
- $c = "# Syntax error.";
- goto NEXT;
- }
- elsif (!@$o) {
- return 0;
- }
- @files = @{ vsplit($k->{Attachment}) };
-
- NEXT:
- $text = Form::compose([[$c, $o, $k, $e]]);
- } while ($error);
+ my ($tmp) = vi_form_while(
+ $text,
+ sub {
+ my ($text, $form) = @_;
+ foreach my $f ( @{ vsplit($form->[2]{'Attachment'}) } ) {
+ return (0, "File '$f' doesn't exist") unless -f $f;
+ }
+ @files = @{ vsplit($form->[2]{'Attachment'}) };
+ return 1;
+ },
+ );
+ return $tmp unless $tmp;
+ $text = $tmp;
}
my $i = 1;
return $passwd;
}
+sub vi_form_while {
+ my $text = shift;
+ my $cb = shift;
+
+ my $error = 0;
+ my ($c, $o, $k, $e);
+ do {
+ my $ntext = vi($text);
+ return undef if ($error && $ntext eq $text);
+
+ $text = $ntext;
+
+ my $form = Form::parse($text);
+ $error = 0;
+ ($c, $o, $k, $e) = @{ $form->[0] };
+ if ( $e ) {
+ $error = 1;
+ $c = "# Syntax error.";
+ goto NEXT;
+ }
+ elsif (!@$o) {
+ return 0;
+ }
+
+ my ($status, $msg) = $cb->( $text, [$c, $o, $k, $e] );
+ unless ( $status ) {
+ $error = 1;
+ $c = "# $msg";
+ }
+
+ NEXT:
+ $text = Form::compose([[$c, $o, $k, $e]]);
+ } while ($error);
+
+ return $text;
+}
+
sub vi {
my ($text) = @_;
my $editor = $ENV{EDITOR} || $ENV{VISUAL} || "vi";
}
push @words, $s;
}
- elsif ( $a =~ /^q{/ ) {
+ elsif ( $a =~ /^q\{/ ) {
my $s = $a;
- while ( $a !~ /}$/ ) {
+ while ( $a !~ /\}$/ ) {
( $a, $b ) =
split /\s*,\s*/, $b, 2;
$s .= ',' . $a;
}
- $s =~ s/^q{/'/;
- $s =~ s/}/'/;
+ $s =~ s/^q\{/'/;
+ $s =~ s/\}/'/;
push @words, $s;
}
else {
-S var=val
Submits the specified variable with the request.
-t type Specifies object type.
+ -ct content-type Specifies content type of message(tickets only).
Examples:
# Interactive (starts $EDITOR with a form).
rt edit ticket/3
rt create -t ticket
+ rt create -t ticket -ct text/html
# Non-interactive.
rt edit ticket/1-3 add cc=foo@example.com set priority=3 due=tomorrow
Options:
-m <text> Specify comment text.
+ -ct <content-type> Specify content-type of comment text.
-a <file> Attach a file to the comment. (May be used more
than once to attach multiple files.)
-c <addrs> A comma-separated list of Cc addresses.
#! /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.13.
+# Generated by GNU Autoconf 2.68 for RT rt-4.0.19.
#
# Report bugs to <rt-bugs@bestpractical.com>.
#
# Identity of this package.
PACKAGE_NAME='RT'
PACKAGE_TARNAME='rt'
-PACKAGE_VERSION='rt-4.0.13'
-PACKAGE_STRING='RT rt-4.0.13'
+PACKAGE_VERSION='rt-4.0.19'
+PACKAGE_STRING='RT rt-4.0.19'
PACKAGE_BUGREPORT='rt-bugs@bestpractical.com'
PACKAGE_URL=''
# 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.13 to adapt to many kinds of systems.
+\`configure' configures RT rt-4.0.19 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of RT rt-4.0.13:";;
+ short | recursive ) echo "Configuration of RT rt-4.0.19:";;
esac
cat <<\_ACEOF
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-RT configure rt-4.0.13
+RT configure rt-4.0.19
generated by GNU Autoconf 2.68
Copyright (C) 2010 Free Software Foundation, Inc.
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.13, which was
+It was created by RT $as_me rt-4.0.19, which was
generated by GNU Autoconf 2.68. Invocation command line was
$ $0 $@
rt_version_minor=0
-rt_version_patch=13
+rt_version_patch=19
test "x$rt_version_major" = 'x' && rt_version_major=0
test "x$rt_version_minor" = 'x' && rt_version_minor=0
fi
-ac_config_files="$ac_config_files etc/upgrade/3.8-branded-queues-extension etc/upgrade/3.8-ical-extension etc/upgrade/split-out-cf-categories etc/upgrade/generate-rtaddressregexp etc/upgrade/upgrade-articles etc/upgrade/vulnerable-passwords sbin/rt-attributes-viewer sbin/rt-preferences-viewer sbin/rt-session-viewer sbin/rt-dump-metadata sbin/rt-setup-database sbin/rt-test-dependencies sbin/rt-email-digest sbin/rt-email-dashboards sbin/rt-clean-sessions sbin/rt-shredder sbin/rt-validator sbin/rt-validate-aliases sbin/rt-email-group-admin sbin/rt-server sbin/rt-server.fcgi sbin/standalone_httpd sbin/rt-setup-fulltext-index sbin/rt-fulltext-indexer bin/rt-crontool bin/rt-mailgate bin/rt"
+ac_config_files="$ac_config_files etc/upgrade/3.8-ical-extension etc/upgrade/split-out-cf-categories etc/upgrade/generate-rtaddressregexp etc/upgrade/upgrade-articles etc/upgrade/vulnerable-passwords sbin/rt-attributes-viewer sbin/rt-preferences-viewer sbin/rt-session-viewer sbin/rt-dump-metadata sbin/rt-setup-database sbin/rt-test-dependencies sbin/rt-email-digest sbin/rt-email-dashboards sbin/rt-clean-sessions sbin/rt-shredder sbin/rt-validator sbin/rt-validate-aliases sbin/rt-email-group-admin sbin/rt-server sbin/rt-server.fcgi sbin/standalone_httpd sbin/rt-setup-fulltext-index sbin/rt-fulltext-indexer bin/rt-crontool bin/rt-mailgate bin/rt"
ac_config_files="$ac_config_files Makefile etc/RT_Config.pm lib/RT/Generated.pm t/data/configs/apache2.2+mod_perl.conf t/data/configs/apache2.2+fastcgi.conf"
# 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.13, which was
+This file was extended by RT $as_me rt-4.0.19, which was
generated by GNU Autoconf 2.68. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
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.13
+RT config.status rt-4.0.19
configured by $0, generated by GNU Autoconf 2.68,
with options \\"\$ac_cs_config\\"
for ac_config_target in $ac_config_targets
do
case $ac_config_target in
- "etc/upgrade/3.8-branded-queues-extension") CONFIG_FILES="$CONFIG_FILES etc/upgrade/3.8-branded-queues-extension" ;;
"etc/upgrade/3.8-ical-extension") CONFIG_FILES="$CONFIG_FILES etc/upgrade/3.8-ical-extension" ;;
"etc/upgrade/split-out-cf-categories") CONFIG_FILES="$CONFIG_FILES etc/upgrade/split-out-cf-categories" ;;
"etc/upgrade/generate-rtaddressregexp") CONFIG_FILES="$CONFIG_FILES etc/upgrade/generate-rtaddressregexp" ;;
case $ac_file$ac_mode in
- "etc/upgrade/3.8-branded-queues-extension":F) chmod ug+x $ac_file
- ;;
"etc/upgrade/3.8-ical-extension":F) chmod ug+x $ac_file
;;
"etc/upgrade/split-out-cf-categories":F) chmod ug+x $ac_file
dnl Binaries that should be +x
AC_CONFIG_FILES([
- etc/upgrade/3.8-branded-queues-extension
etc/upgrade/3.8-ical-extension
etc/upgrade/split-out-cf-categories
etc/upgrade/generate-rtaddressregexp
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
COPYRIGHT:
-This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
<sales@bestpractical.com>
(Except where explicitly superseded by other copyright notices)
my @MAKE = qw(Makefile);
File::Find::find({ no_chdir => 1, wanted => \&tag_pm}, 'lib');
-File::Find::find({ no_chdir => 1, wanted => \&tag_mason}, 'share/html');
-File::Find::find({ no_chdir => 1, wanted => \&tag_script}, 'sbin');
-File::Find::find({ no_chdir => 1, wanted => \&tag_script}, 'bin');
-File::Find::find({ no_chdir => 1, wanted => \&tag_script}, 'etc/upgrade');
-File::Find::find({ no_chdir => 1, wanted => \&tag_script}, 'devel/tools');
-tag_makefile ('Makefile.in');
+for my $masondir (qw( html share/html )) {
+ next unless -d $masondir;
+ File::Find::find({ no_chdir => 1, wanted => \&tag_mason}, $masondir);
+}
+for my $bindir (qw( sbin bin etc/upgrade devel/tools )) {
+ next unless -d $bindir;
+ File::Find::find({ no_chdir => 1, wanted => \&tag_script}, $bindir);
+}
+tag_makefile ('Makefile.in') if -f 'Makefile.in';
tag_makefile ('README');
my $pmlic = $LICENSE;
$pmlic =~ s/^/%# /mg;
$pmlic =~ s/\s*$//mg;
- if ($file =~ /^%# BEGIN BPS TAGGED BLOCK {{{/ms) {
+ if ($file =~ /^%# BEGIN BPS TAGGED BLOCK \{\{\{/ms) {
print "has license section";
- $file =~ s/^%# BEGIN BPS TAGGED BLOCK {{{(.*?)%# END BPS TAGGED BLOCK }}}/%# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n%# END BPS TAGGED BLOCK }}}/ms;
+ $file =~ s/^%# BEGIN BPS TAGGED BLOCK \{\{\{(.*?)%# END BPS TAGGED BLOCK \}\}\}/%# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n%# END BPS TAGGED BLOCK }}}/ms;
} else {
print "no license section";
$file ="%# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n%# END BPS TAGGED BLOCK }}}\n". $file;
}
- $file =~ s/%# END BPS TAGGED BLOCK }}}(\n+)/%# END BPS TAGGED BLOCK }}}\n/mg;
+ $file =~ s/%# END BPS TAGGED BLOCK \}\}\}(\n+)/%# END BPS TAGGED BLOCK }}}\n/mg;
print "\n";
my $pmlic = $LICENSE;
$pmlic =~ s/^/# /mg;
$pmlic =~ s/\s*$//mg;
- if ($file =~ /^# BEGIN BPS TAGGED BLOCK {{{/ms) {
+ if ($file =~ /^# BEGIN BPS TAGGED BLOCK \{\{\{/ms) {
print "has license section";
- $file =~ s/^# BEGIN BPS TAGGED BLOCK {{{(.*?)# END BPS TAGGED BLOCK }}}/# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n# END BPS TAGGED BLOCK }}}/ms;
+ $file =~ s/^# BEGIN BPS TAGGED BLOCK \{\{\{(.*?)# END BPS TAGGED BLOCK \}\}\}/# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n# END BPS TAGGED BLOCK }}}/ms;
} else {
print "no license section";
$file ="# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n# END BPS TAGGED BLOCK }}}\n". $file;
}
- $file =~ s/# END BPS TAGGED BLOCK }}}(\n+)/# END BPS TAGGED BLOCK }}}\n/mg;
+ $file =~ s/# END BPS TAGGED BLOCK \}\}\}(\n+)/# END BPS TAGGED BLOCK }}}\n/mg;
print "\n";
sub tag_pm {
my $pm = $_;
- next unless $pm =~ /\.pm/s;
+ return unless $pm =~ /\.pm/s;
open( FILE, '<', $pm ) or die "Failed to open $pm";
my $file = (join "", <FILE>);
close (FILE);
my $pmlic = $LICENSE;
$pmlic =~ s/^/# /mg;
$pmlic =~ s/\s*$//mg;
- if ($file =~ /^# BEGIN BPS TAGGED BLOCK {{{/ms) {
+ if ($file =~ /^# BEGIN BPS TAGGED BLOCK \{\{\{/ms) {
print "has license section";
- $file =~ s/^# BEGIN BPS TAGGED BLOCK {{{(.*?)# END BPS TAGGED BLOCK }}}/# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n# END BPS TAGGED BLOCK }}}/ms;
+ $file =~ s/^# BEGIN BPS TAGGED BLOCK \{\{\{(.*?)# END BPS TAGGED BLOCK \}\}\}/# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n# END BPS TAGGED BLOCK }}}/ms;
} else {
print "no license section";
$file ="# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n# END BPS TAGGED BLOCK }}}\n". $file;
}
- $file =~ s/# END BPS TAGGED BLOCK }}}(\n+)/# END BPS TAGGED BLOCK }}}\n\n/mg;
+ $file =~ s/# END BPS TAGGED BLOCK \}\}\}(\n+)/# END BPS TAGGED BLOCK }}}\n\n/mg;
print "\n";
my $pmlic = $LICENSE;
$pmlic =~ s/^/# /msg;
$pmlic =~ s/\s*$//mg;
- if ($file =~ /^# BEGIN BPS TAGGED BLOCK {{{/ms) {
+ if ($file =~ /^# BEGIN BPS TAGGED BLOCK \{\{\{/ms) {
print "has license section";
- $file =~ s/^# BEGIN BPS TAGGED BLOCK {{{(.*?)# END BPS TAGGED BLOCK }}}/# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n# END BPS TAGGED BLOCK }}}/ms;
+ $file =~ s/^# BEGIN BPS TAGGED BLOCK \{\{\{(.*?)# END BPS TAGGED BLOCK \}\}\}/# BEGIN BPS TAGGED BLOCK {{{\n$pmlic\n# END BPS TAGGED BLOCK }}}/ms;
} else {
}
}
- $file =~ s/# END BPS TAGGED BLOCK }}}(\n+)/# END BPS TAGGED BLOCK }}}\n/mg;
+ $file =~ s/# END BPS TAGGED BLOCK \}\}\}(\n+)/# END BPS TAGGED BLOCK }}}\n/mg;
print "\n";
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
been assigned an ID of { $Ticket->SubjectTag }."
If you were previously using RT::Extension::BrandedQueues, you MUST uninstall
-it before upgrading. In addition, you must run the
-'etc/upgrade/3.8-branded-queues-extension' perl script. This will
-convert the extension's configuration into the new format. Finally, in
-templates where you were using the Tag method ($Ticket->QueueObj->Tag),
-you will need to replace it with $Ticket->SubjectTag
+it before upgrading.
RT::Action::LinearEscalate extension has been integrated into core, so you
MUST uninstall it before upgrading.
=back
+=head2 Ticket content searches (full text search)
+
+Since 4.0.0, RT's ticket content search is disabled by default because of
+performance issues when used without full text indexing. For details on how to
+re-enable it with (or without) full text indexing, see
+F<docs/full_text_indexing.pod>.
+
+
=head1 UPGRADING FROM 4.0.5 AND EARLIER
Site-specific custom types (anything but ticket, reminder or approval)
are not affected by these changes.
+
+=head1 UPGRADING FROM 4.0.13 AND EARLIER
+
+=head2 Outgoing mail From: header
+
+The "Default" key of the C<$OverrideOutgoingMailFrom> config option now,
+as previously documented, only applies when no ticket is involved.
+Previously it was also used when a ticket was involved but the
+associated queue had no specific correspond address. In such cases the
+global correspond address is now used.
+
+The config option C<$SetOutgoingMailFrom> now accepts an email address
+as a value which will act as a global default. This covers the simple
+case of sending all bounces to a specific address, without the previous
+solution of resorting to defining all queues in
+$OverrideOutgoingMailFrom. Any definitions in the Override option
+(including Default) still take precedence. See
+L<RT_Config/$SetOutgoingMailFrom> for more information.
+
+=head2 Reminder statuses
+
+New reminders are now created in the "reminder_on_open" status defined in your
+lifecycles. For the default lifecycle, this means reminders will start as
+"open" instead of "new". This change is for consistency when a completed
+reminder is reopened at a later date. If you use custom lifecycles and added
+further transition restrictions, you may need to adjust the L<"reminder_on_open"
+setting|RT_Config/reminder_on_open> in your lifecycles.
+
+=head2 Bookmarks
+
+Previously, the list of Bookmarks on your homepage was unlimited (if you
+had 100 bookmarked tickets, you would see a 100 item list on your RT at
+a Glance). 'Bookmarked Tickets' now uses the same size limits as any
+other search on your homepage. This can be customized using the 'Rows
+per box' setting on your RT at a Glance configuration page.
+
+=head2 PostgreSQL 9.2
+
+If you are upgrading an RT from 3.8 (or earlier) to 4.0 on PostgreSQL
+9.2, you should make sure that you have installed DBD::Pg 2.19.3 or
+higher. If you start your upgrade without installing a recent-enough
+version of DBD::Pg RT will stop the upgrade during the 3.9.8 step and
+remind you to upgrade DBD::Pg. If this happens, you can re-start your
+upgrade by running:
+
+ ./sbin/rt-setup-database --action insert --datadir etc/upgrade/3.9.8/
+
+Followed by re-running make upgrade-database and answering 3.9.8 when
+prompted for which RT version you're upgrading from.
perl etc/upgrade/generate-rtaddressregexp
-If left blank, RT will generate a regexp for you, based on your
-comment and correspond address settings on your queues; this comes at
-a small cost in start-up speed.
+If left blank, RT will compare each address to your configured
+C<$CorrespondAddress> and C<$CommentAddress> before searching for a
+Queue configured with a matching "Reply Address" or "Comment Address"
+on the Queue Admin page.
=cut
to a temporary file. RT will log the location of the temporary file
so you can extract mail from it afterward.
+On shutdown, RT will clean up the temporary file created when using
+the 'testfile' option. If testing while the RT server is still running,
+you can find the files in the location noted in the log file. If you run
+a tool like C<rt-crontool> however, or if you look after stopping the server,
+the files will have been deleted when the process completed. If you need to
+keep the files for development or debugging, you can manually set
+C<< UNLINK => 0 >> where the testfile config is processed in
+F<lib/RT/Interface/Email.pm>.
+
=cut
#Set($MailCommand, "sendmailpipe");
Warning: If you use this setting, bounced mails will appear to be
incoming mail to the system, thus creating new tickets.
+If the value contains an C<@>, it is assumed to be an email address and used as
+a global envelope sender. Expected usage in this case is to simply set the
+same envelope sender on all mail from RT, without defining
+C<$OverrideOutgoingMailFrom>. If you do define C<$OverrideOutgoingMailFrom>,
+anything specified there overrides the global value (including Default).
+
This option only works if C<$MailCommand> is set to 'sendmailpipe'.
=cut
OwnerName,
Priority,
'__NEWLINE__',
- '',
+ '__NBSP__',
'<small>__Requestors__</small>',
'<small>__CustomerTags__</small>',
'<small>__CreatedRelative__</small>',
If C<$ParseNewMessageForTicketCcs> is set to 1, RT will attempt to
divine Ticket 'Cc' watchers from the To and Cc lines of incoming
-messages. Be forewarned that if you have I<any> addresses which forward
-mail to RT automatically and you enable this option without modifying
-C<$RTAddressRegexp> below, you will get yourself into a heap of trouble.
+messages that create new Tickets. This option does not apply to replies
+or comments on existing Tickets. Be forewarned that if you have I<any>
+addresses which forward mail to RT automatically and you enable this
+option without modifying C<$RTAddressRegexp> below, you will get
+yourself into a heap of trouble.
=cut
# 'auto-key-locate' => 'keyserver',
# enables the automatic retrieving of keys when verifying signatures
-# 'auto-key-retrieve' => undef,
+# 'keyserver-options' => 'auto-key-retrieve',
);
=back
id INTEGER NOT NULL AUTO_INCREMENT,
Name varchar(200) NOT NULL ,
Description varchar(255) NULL ,
- CorrespondAddress varchar(120) CHARACTER SET ascii NULL,
- CommentAddress varchar(120) CHARACTER SET ascii NULL,
- Lifecycle varchar(32) CHARACTER SET ascii NULL,
+ CorrespondAddress varchar(120) NULL,
+ CommentAddress varchar(120) NULL,
+ Lifecycle varchar(32) NULL,
SubjectTag varchar(120) NULL,
InitialPriority integer NOT NULL DEFAULT 0 ,
FinalPriority integer NOT NULL DEFAULT 0 ,
sub gen_scrip_description {
my $scrip = shift;
- my $condition = $scrip->ConditionObj->Name
+
+ my $condition;
+ eval{
+ $condition = $scrip->ConditionObj->Name
|| $scrip->ConditionObj->Description
- || ('On Condition #'. $scrip->Condition);
+ || ('On Condition #'. $scrip->Condition);
+ };
+
+ if ($@){
+ print STDERR $@;
+ print STDERR "Reference to missing scrip condition found. If you have ScripCondition = 0 in the Scrips table, update with a real condition number.\n";
+ $condition = 'On undefined Condition # 0';
+ }
+
my $action = $scrip->ActionObj->Name
|| $scrip->ActionObj->Description
|| ('Run Action #'. $scrip->Action);
return join ' ', $condition, $action;
-}
+ }
}
1;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@Initial = sub {
- my $dbh = $RT::Handle->dbh;
- my $sth = $dbh->table_info( '', undef, undef, "'TABLE'");
my $found_fm_tables = {};
- while ( my $table = $sth->fetchrow_hashref ) {
- my $name = $table->{TABLE_NAME} || $table->{table_name};
+ foreach my $name ( $RT::Handle->_TableNames ) {
next unless $name =~ /^fm_/i;
$found_fm_tables->{lc $name}++;
}
$RT::Logger->error("We found RTFM tables in your database. Checking for content.");
+ my $dbh = $RT::Handle->dbh;
my $result = $dbh->selectall_arrayref("SELECT count(*) AS articlecount FROM FM_Articles", { Slice => {} } );
if ($result->[0]{articlecount} > 0) {
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
my $dbh = $RT::Handle->dbh;
-my $sth = $dbh->table_info( '', undef, undef, "'TABLE'");
my $found_fm_tables;
-while ( my $table = $sth->fetchrow_hashref ) {
- my $name = $table->{TABLE_NAME} || $table->{'table_name'}; # Oracle's table_info affected by NAME_lc
+foreach my $name ( $RT::Handle->_TableNames ) {
next unless $name =~ /^fm_/i;
$found_fm_tables->{lc $name}++;
}
use RT::CustomFields;
my $cfs = RT::CustomFields->new(RT->SystemUser);
$cfs->Limit( FIELD => 'LookupType', VALUE => 'RT::FM::Class-RT::FM::Article' );
+ $cfs->{'find_disabled_rows'} = 1;
while ( my $cf = $cfs->Next ) {
my ($ret, $msg) = $cf->__Set( Field => 'LookupType', Value => 'RT::Class-RT::Article' );
warn "Update Custom Field LookupType for CF.".$cf->Id." $msg";
use RT::ObjectCustomFieldValues;
my $ocfvs = RT::ObjectCustomFieldValues->new(RT->System);
$ocfvs->Limit( FIELD => 'ObjectType', VALUE => 'RT::FM::Article' );
+ $ocfvs->{'find_expired_rows'} = 1;
while ( my $ocfv = $ocfvs->Next ) {
my ($ret, $msg) = $ocfv->__Set( Field => 'ObjectType', Value => 'RT::Article' );
warn "Updated CF ".$ocfv->__Value('CustomField')." Value for Article ".$ocfv->__Value('ObjectId');
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
my $dbh = $RT::Handle->dbh;
-my $sth = $dbh->table_info( '', undef, undef, "'TABLE'");
my $found_fm_tables;
-while ( my $table = $sth->fetchrow_hashref ) {
- my $name = $table->{TABLE_NAME} || $table->{'table_name'}; # Oracle's table_info affected by NAME_lc
+foreach my $name ( $RT::Handle->_TableNames ) {
next unless $name =~ /^fm_/i;
$found_fm_tables->{lc $name}++;
}
use RT::CustomFields;
my $cfs = RT::CustomFields->new(RT->SystemUser);
$cfs->Limit( FIELD => 'LookupType', VALUE => 'RT::FM::Class-RT::FM::Article' );
+ $cfs->{'find_disabled_rows'} = 1;
while ( my $cf = $cfs->Next ) {
my ($ret, $msg) = $cf->__Set( Field => 'LookupType', Value => 'RT::Class-RT::Article' );
warn "Update Custom Field LookupType for CF.".$cf->Id." $msg";
use RT::ObjectCustomFieldValues;
my $ocfvs = RT::ObjectCustomFieldValues->new(RT->System);
$ocfvs->Limit( FIELD => 'ObjectType', VALUE => 'RT::FM::Article' );
+ $ocfvs->{'find_expired_rows'} = 1;
while ( my $ocfv = $ocfvs->Next ) {
my ($ret, $msg) = $ocfv->__Set( Field => 'ObjectType', Value => 'RT::Article' );
warn "Updated CF ".$ocfv->__Value('CustomField')." Value for Article ".$ocfv->__Value('ObjectId');
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
Queues => {
Name => 'utf8',
Description => 'utf8',
- CorrespondAddress => 'ascii',
- CommentAddress => 'ascii',
+ CorrespondAddress => 'utf8',
+ CommentAddress => 'utf8',
},
ScripActions => {
Name => 'utf8',
Password => 'binary',
Comments => 'utf8',
Signature => 'utf8',
- EmailAddress => 'ascii',
+ EmailAddress => 'utf8',
FreeformContactInfo => 'utf8',
Organization => 'utf8',
RealName => 'utf8',
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
=cut
sub Init {
-
- my @arg = @_;
+ shift if @_%2; # code is inconsistent about calling as method
+ my %args = (@_);
CheckPerlRequirements();
#Get a database connection
ConnectToDatabase();
InitSystemObjects();
- InitClasses();
- InitLogging(@arg);
+ InitClasses(%args);
+ InitLogging(%args);
InitPlugins();
RT::I18N->Init;
RT->Config->PostLoadCheck;
my ($package, $filename, $line) = caller($frame);
$p{'message'} =~ s/(?:\r*\n)+$//;
- return "[". gmtime(time) ."] [". $p{'level'} ."]: "
+ return "[$$] [". gmtime(time) ."] [". $p{'level'} ."]: "
. $p{'message'} ." ($filename:$line)\n";
};
$p{message} =~ s/(?:\r*\n)+$//;
if ($p{level} eq 'debug') {
- return "$p{message}\n";
+ return "[$$] $p{message} ($filename:$line)\n";
} else {
- return "$p{message} ($filename:$line)\n";
+ return "[$$] $p{message}\n";
}
};
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
=head2 AddAttachment $attachment
-Takes one attachment object of L<RT::Attachmment> class and attaches it to the message
+Takes one attachment object of L<RT::Attachment> class and attaches it to the message
we're building.
=cut
and $attach->TransactionObj->CurrentUserCanSee;
# ->attach expects just the disposition type; extract it if we have the header
+ # or default to "attachment"
my $disp = ($attach->GetHeader('Content-Disposition') || '')
- =~ /^\s*(inline|attachment)/i ? $1 : undef;
+ =~ /^\s*(inline|attachment)/i ? $1 : "attachment";
$MIMEObj->attach(
Type => $attach->ContentType,
Charset => $attach->OriginalEncoding,
Data => $attach->OriginalContent,
- Disposition => $disp, # a false value defaults to inline in MIME::Entity
+ Disposition => $disp,
Filename => $self->MIMEEncodeString( $attach->Filename ),
'RT-Attachment:' => $self->TicketObj->Id . "/"
. $self->TransactionObj->Id . "/"
$subject =~ s/(\r\n|\n|\s)/ /g;
- $self->SetHeader( 'Subject', $subject );
+ $self->SetHeader( 'Subject', Encode::encode_utf8( $subject ) );
}
my $self = shift;
my $head = $self->TemplateObj->MIMEObj->head;
- $head->replace(
- Subject => RT::Interface::Email::AddSubjectTag(
- Encode::decode_utf8( $head->get('Subject') ),
- $self->TicketObj,
- ),
+ $self->SetHeader(
+ Subject =>
+ Encode::encode_utf8(
+ RT::Interface::Email::AddSubjectTag(
+ Encode::decode_utf8( $head->get('Subject') ),
+ $self->TicketObj,
+ ),
+ ),
);
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
require Time::ParseDate;
foreach my $date (qw(Created< Created> LastUpdated< LastUpdated>)) {
next unless ( $args{$date} );
- my $seconds = Time::ParseDate::parsedate( $args{$date}, FUZZY => 1, PREFER_PAST => 1 );
+ my ($seconds, $error) = Time::ParseDate::parsedate( $args{$date}, FUZZY => 1, PREFER_PAST => 1 );
+ unless ( defined $seconds ) {
+ $RT::Logger->warning(
+ "Couldn't parse date '$args{$date}' by Time::ParseDate" );
+ }
my $date_obj = RT::Date->new( $self->CurrentUser );
$date_obj->Set( Format => 'unix', Value => $seconds );
$dates->{$date} = $date_obj;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@_);
if ($args{Object} and UNIVERSAL::can($args{Object}, 'Id')) {
- $args{ObjectType} = ref($args{Object});
+ $args{ObjectType} = $args{Object}->isa("RT::CurrentUser") ? "RT::User" : ref($args{Object});
$args{ObjectId} = $args{Object}->Id;
} else {
return(0, $self->loc("Required parameter '[_1]' not specified", 'Object'));
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
unless (eval { $obj->id} ){
return undef;
}
- $self->Limit(FIELD => 'ObjectType', OPERATOR=> '=', VALUE => ref($obj), ENTRYAGGREGATOR => 'OR');
+
+ my $type = $obj->isa("RT::CurrentUser") ? "RT::User" : ref($obj);
+
+ $self->Limit(FIELD => 'ObjectType', OPERATOR=> '=', VALUE => $type, ENTRYAGGREGATOR => 'OR');
$self->Limit(FIELD => 'ObjectId', OPERATOR=> '=', VALUE => $obj->id, ENTRYAGGREGATOR => 'OR', QUOTEVALUE => 0);
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
sub Table {'Classes'}
+=head2 _Init
+
+=cut
+
+ sub _Init {
+ my $self = shift;
+ $self->{'with_disabled_column'} = 1;
+ return ($self->SUPER::_Init(@_));
+ }
=head2 Next
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
}
$RT::Logger->debug( $res{'status'} ) if $res{'status'};
$RT::Logger->warning( $res{'stderr'} ) if $res{'stderr'};
- $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
+ if ( $res{'logger'} && $? ) {
+ $RT::Logger->error( $res{'logger'} );
+ $RT::Logger->error( 'The above error may result from an unconfigured RT/GPG installation. See perldoc etc/RT_Config.pm for information about configuring or disabling GPG support for RT' );
+ }
if ( $@ || $? ) {
$res{'message'} = $@? $@: "gpg exitted with error code ". ($? >> 8);
return %res;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
use strict;
use warnings;
-
+use Scalar::Util 'blessed';
use base 'RT::Record';
$self->FriendlyType));
}
- # XXX: Remove this restriction once we support lists and cascaded selects
- if ( $self->BasedOnObj->id and $type =~ /List/ ) {
- return (0, $self->loc("We can't currently render as a List when basing categories on another custom field. Please use another render type."));
- }
-
return $self->_Set( Field => 'RenderType', Value => $type, @_ );
}
sub LookupTypes {
my $self = shift;
- return keys %FRIENDLY_OBJECT_TYPES;
+ return sort keys %FRIENDLY_OBJECT_TYPES;
}
my @FriendlyObjectTypes = (
return ( $self->loc( $FriendlyObjectTypes[$#types], @types ) );
}
+=head1 RecordClassFromLookupType
+
+Returns the type of Object referred to by ObjectCustomFields' ObjectId column
+
+Optionally takes a LookupType to use instead of using the value on the loaded
+record. In this case, the method may be called on the class instead of an
+object.
+
+=cut
+
sub RecordClassFromLookupType {
my $self = shift;
- my ($class) = ($self->LookupType =~ /^([^-]+)/);
+ my $type = shift || $self->LookupType;
+ my ($class) = ($type =~ /^([^-]+)/);
unless ( $class ) {
- $RT::Logger->error(
- "Custom Field #". $self->id
- ." has incorrect LookupType '". $self->LookupType ."'"
- );
+ if (blessed($self) and $self->LookupType eq $type) {
+ $RT::Logger->error(
+ "Custom Field #". $self->id
+ ." has incorrect LookupType '$type'"
+ );
+ } else {
+ RT->Logger->error("Invalid LookupType passed as argument: $type");
+ }
+ return undef;
+ }
+ return $class;
+}
+
+=head1 ObjectTypeFromLookupType
+
+Returns the ObjectType used in ObjectCustomFieldValues rows for this CF
+
+Optionally takes a LookupType to use instead of using the value on the loaded
+record. In this case, the method may be called on the class instead of an
+object.
+
+=cut
+
+sub ObjectTypeFromLookupType {
+ my $self = shift;
+ my $type = shift || $self->LookupType;
+ my ($class) = ($type =~ /([^-]+)$/);
+ unless ( $class ) {
+ if (blessed($self) and $self->LookupType eq $type) {
+ $RT::Logger->error(
+ "Custom Field #". $self->id
+ ." has incorrect LookupType '$type'"
+ );
+ } else {
+ RT->Logger->error("Invalid LookupType passed as argument: $type");
+ }
return undef;
}
return $class;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
use base qw(RT::CustomFieldValues::External);
+=head1 NAME
+
+RT::CustomFieldValues::Groups - Provide RT's groups as a dynamic list of CF values
+
+=head1 SYNOPSIS
+
+To use as a source of CF values, add the following to your F<RT_SiteConfig.pm>
+and restart RT.
+
+ # In RT_SiteConfig.pm
+ Set( @CustomFieldValuesSources, "RT::CustomFieldValues::Groups" );
+
+Then visit the modify CF page in the RT admin configuration.
+
+=head1 METHODS
+
+Most methods are inherited from L<RT::CustomFieldValues::External>, except the
+ones below.
+
+=head2 SourceDescription
+
+Returns a brief string describing this data source.
+
+=cut
+
sub SourceDescription {
return 'RT user defined groups';
}
+=head2 ExternalValues
+
+Returns an arrayref containing a hashref for each possible value in this data
+source, where the value name is the group name.
+
+=cut
+
sub ExternalValues {
my $self = shift;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
$self->Limit( FIELD => 'LookupType', STARTSWITH => "$lookup" );
}
+=head2 LimitToObjectId
+
+Takes an ObjectId and limits the collection to CFs applied to said object.
+
+When called multiple times the ObjectId limits are joined with OR.
+
+=cut
+
+sub LimitToObjectId {
+ my $self = shift;
+ my $id = shift;
+ $self->Limit(
+ ALIAS => $self->_OCFAlias,
+ FIELD => 'ObjectId',
+ OPERATOR => '=',
+ VALUE => $id || 0,
+ ENTRYAGGREGATOR => 'OR'
+ );
+}
=head2 LimitToGlobalOrObjectId
foreach my $id (@_) {
- $self->Limit( ALIAS => $self->_OCFAlias,
- FIELD => 'ObjectId',
- OPERATOR => '=',
- VALUE => $id || 0,
- ENTRYAGGREGATOR => 'OR' );
- $global_only = 0 if $id;
+ $self->LimitToObjectId($id);
+ $global_only = 0 if $id;
}
- $self->Limit( ALIAS => $self->_OCFAlias,
- FIELD => 'ObjectId',
- OPERATOR => '=',
- VALUE => 0,
- ENTRYAGGREGATOR => 'OR' ) unless $global_only;
+ $self->LimitToObjectId(0) unless $global_only;
}
sub _LimitToOCFs {
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
my $sub_hour = $subscription->SubValue('Hour');
my $sub_dow = $subscription->SubValue('Dow');
my $sub_dom = $subscription->SubValue('Dom');
- my $sub_fow = $subscription->SubValue('Fow');
+ my $sub_fow = $subscription->SubValue('Fow') || 1;
my ($hour, $dow, $dom) = @{ $args{LocalTime} };
return 0 if $sub_dow ne $dow;
# does it match the "every N weeks" clause?
- $sub_fow = 1 if !$sub_fow;
-
return 1 if $counter % $sub_fow == 0;
$subscription->SetSubValues(Counter => $counter + 1)
Type => 'text/html',
Charset => 'UTF-8',
Disposition => 'inline',
+ Encoding => "base64",
);
for my $part (@parts) {
$entity->add_part($part);
}
+ $entity->make_singlepart;
+
return $entity;
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
# should be applied to absolute times, so compensate shift in NOW
my $now = time;
$now += ($self->Localtime( $args{Timezone}, $now ))[9];
- my $date = Time::ParseDate::parsedate(
+ my ($date, $error) = Time::ParseDate::parsedate(
$args{'Value'},
GMT => 1,
NOW => $now,
PREFER_PAST => RT->Config->Get('AmbiguousDayInPast'),
PREFER_FUTURE => RT->Config->Get('AmbiguousDayInFuture'),
);
+ unless ( defined $date ) {
+ $RT::Logger->warning(
+ "Couldn't parse date '$args{'Value'}' by Time::ParseDate"
+ );
+ return $self->Unix(0);
+ }
+
# apply timezone offset
$date -= ($self->Localtime( $args{Timezone}, $date ))[9];
my ($date, $time) = ('','');
$date .= "$DAYS_OF_WEEK[$wday], " if $args{'DayOfWeek'} && $args{'Date'};
- $date .= "$mday $MONTHS[$mon] $year" if $args{'Date'};
+ $date .= sprintf("%02d %s %04d", $mday, $MONTHS[$mon], $year) if $args{'Date'};
if ( $args{'Time'} ) {
$time .= sprintf("%02d:%02d", $hour, $min);
Date => 1, Time => 1,
@_,
);
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime( 'utc' );
-
- #the month needs incrementing, as gmtime returns 0-11
- $mon++;
my $res;
if ( $args{'Date'} && !$args{'Time'} ) {
- $res = sprintf( '%04d%02d%02d', $year, $mon, $mday );
- }
- elsif ( !$args{'Date'} && $args{'Time'} ) {
+ my (undef, undef, undef, $mday, $mon, $year) =
+ $self->Localtime( 'user' );
+ $res = sprintf( '%04d%02d%02d', $year, $mon+1, $mday );
+ } elsif ( !$args{'Date'} && $args{'Time'} ) {
+ my ($sec, $min, $hour) =
+ $self->Localtime( 'utc' );
$res = sprintf( 'T%02d%02d%02dZ', $hour, $min, $sec );
- }
- else {
- $res = sprintf( '%04d%02d%02dT%02d%02d%02dZ', $year, $mon, $mday, $hour, $min, $sec );
+ } else {
+ my ($sec, $min, $hour, $mday, $mon, $year) =
+ $self->Localtime( 'utc' );
+ $res = sprintf( '%04d%02d%02dT%02d%02d%02dZ', $year, $mon+1, $mday, $hour, $min, $sec );
}
return $res;
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
# accommodate this by pausing and retrying.
last
if ( $fh, $temp_file ) =
- eval { File::Temp::tempfile( undef, UNLINK => 0 ) };
+ eval { File::Temp::tempfile( UNLINK => 0 ) };
sleep 1;
}
if ($fh) {
@addresses = Email::Address->parse($address_string);
}
+ $self->CleanupAddresses(@addresses);
+
return @addresses;
}
+=head2 CleanupAddresses ARRAY
+
+Massages an array of L<Email::Address> objects to make their email addresses
+more palatable.
+
+Currently this strips off surrounding single quotes around C<< ->address >> and
+B<< modifies the L<Email::Address> objects in-place >>.
+
+Returns the list of objects for convienence in C<map>/C<grep> chains.
+
+=cut
+
+sub CleanupAddresses {
+ my $self = shift;
+
+ for my $addr (@_) {
+ next unless defined $addr;
+ # Outlook sometimes sends addresses surrounded by single quotes;
+ # clean them all up
+ if ((my $email = $addr->address) =~ s/^'(.+)'$/$1/) {
+ $addr->address($email);
+ }
+ }
+ return @_;
+}
+
=head2 RescueOutlook
Outlook 2007/2010 have a bug when you write an email with the html format.
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
use warnings;
use strict;
-our $VERSION = '4.0.13';
+our $VERSION = '4.0.19';
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
$self->SUPER::Connect(
User => RT->Config->Get('DatabaseUser'),
Password => RT->Config->Get('DatabasePassword'),
+ DisconnectHandleOnDestroy => 1,
%args,
);
Port => $db_port,
Driver => $db_type,
RequireSSL => RT->Config->Get('DatabaseRequireSSL'),
- DisconnectHandleOnDestroy => 1,
);
if ( $db_type eq 'Oracle' && $db_host ) {
$args{'SID'} = delete $args{'Database'};
elsif ( $db_type eq 'Pg' ) {
$status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0");
}
+ elsif ( $db_type eq 'mysql' ) {
+ $status = $dbh->do("CREATE DATABASE $db_name DEFAULT CHARACTER SET utf8");
+ }
else {
$status = $dbh->do("CREATE DATABASE $db_name");
}
}
if ( $item->{'BasedOn'} ) {
- if ( $item->{'LookupType'} ) {
+ if ( $item->{'BasedOn'} =~ /^\d+$/) {
+ # Already have an ID -- should be fine
+ } elsif ( $item->{'LookupType'} ) {
my $basedon = RT::CustomField->new($RT::SystemUser);
my ($ok, $msg ) = $basedon->LoadByCols( Name => $item->{'BasedOn'},
LookupType => $item->{'LookupType'} );
push @{$self->{'StatementLog'}} , ([Time::HiRes::time(), $statement, [@bind], $duration, HTML::Mason::Exception->new->as_string]);
}
+
+sub _TableNames {
+ my $self = shift;
+ my $dbh = shift || $self->dbh;
+
+ {
+ local $@;
+ if (
+ $dbh->{Driver}->{Name} eq 'Pg'
+ && $dbh->{'pg_server_version'} >= 90200
+ && !eval { DBD::Pg->VERSION('2.19.3'); 1 }
+ ) {
+ die "You're using PostgreSQL 9.2 or newer. You have to upgrade DBD::Pg module to 2.19.3 or newer: $@";
+ }
+ }
+
+ my @res;
+
+ my $sth = $dbh->table_info( '', undef, undef, "'TABLE'");
+ while ( my $table = $sth->fetchrow_hashref ) {
+ push @res, $table->{TABLE_NAME} || $table->{table_name};
+ }
+
+ return @res;
+}
+
__PACKAGE__->FinalizeDatabaseType;
RT::Base->_ImportOverlays();
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
$enc_str = qq{"$enc_str"}
if $enc_str =~ /[,;]/
and $enc_str !~ /^".*"$/
+ and $prefix !~ /"$/ and $trailing !~ /^"/
and (!$field || $field =~ /^(?:To$|From$|B?Cc$|Content-)/i);
$str .= $prefix . $enc_str . $trailing;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
# SetOutgoingMailFrom and bounces conflict, since they both want -f
if ( $args{'Bounce'} ) {
push @args, shellwords(RT->Config->Get('SendmailBounceArguments'));
- } elsif ( RT->Config->Get('SetOutgoingMailFrom') ) {
- my $OutgoingMailAddress;
+ } elsif ( my $MailFrom = RT->Config->Get('SetOutgoingMailFrom') ) {
+ my $OutgoingMailAddress = $MailFrom =~ /\@/ ? $MailFrom : undef;
+ my $Overrides = RT->Config->Get('OverrideOutgoingMailFrom') || {};
if ($TicketObj) {
my $QueueName = $TicketObj->QueueObj->Name;
- my $QueueAddressOverride = RT->Config->Get('OverrideOutgoingMailFrom')->{$QueueName};
+ my $QueueAddressOverride = $Overrides->{$QueueName};
if ($QueueAddressOverride) {
$OutgoingMailAddress = $QueueAddressOverride;
} else {
- $OutgoingMailAddress = $TicketObj->QueueObj->CorrespondAddress;
+ $OutgoingMailAddress ||= $TicketObj->QueueObj->CorrespondAddress
+ || RT->Config->Get('CorrespondAddress');
}
}
-
- $OutgoingMailAddress ||= RT->Config->Get('OverrideOutgoingMailFrom')->{'Default'};
+ elsif ($Overrides->{'Default'}) {
+ $OutgoingMailAddress = $Overrides->{'Default'};
+ }
push @args, "-f", $OutgoingMailAddress
if $OutgoingMailAddress;
&& !IgnoreCcAddress( $_ )
}
map lc $user->CanonicalizeEmailAddress( $_->address ),
- map Email::Address->parse( $args{'Head'}->get( $_ ) ),
+ map RT::EmailParser->CleanupAddresses( Email::Address->parse( $args{'Head'}->get( $_ ) ) ),
qw(To Cc);
}
my $head = $Message->head;
my $ErrorsTo = ParseErrorsToAddressFromHead( $head );
+ my $Sender = (ParseSenderAddressFromHead( $head ))[0];
+ my $From = $head->get("From");
+ chomp $From if defined $From;
my $MessageId = $head->get('Message-ID')
|| "<no-message-id-". time . rand(2000) .'@'. RT->Config->Get('Organization') .'>';
);
return (
0,
- "$ErrorsTo tried to submit a message to "
+ ($CurrentUser->EmailAddress || $CurrentUser->Name)
+ . " ($Sender) tried to submit a message to "
. $args{'Queue'}
. " without permission.",
undef
Explanation => $ErrStr,
MIMEObj => $Message
);
- return ( 0, "Ticket creation failed: $ErrStr", $Ticket );
+ return ( 0, "Ticket creation From: $From failed: $ErrStr", $Ticket );
}
# strip comments&corresponds from the actions we don't need
Explanation => $msg,
MIMEObj => $Message
);
- return ( 0, "Message not recorded: $msg", $Ticket );
+ return ( 0, "Message From: $From not recorded: $msg", $Ticket );
}
} elsif ($unsafe_actions) {
my ( $status, $msg ) = _RunUnsafeAction(
@_
);
+ my $From = $args{Message}->head->get("From");
+
if ( $args{'Action'} =~ /^take$/i ) {
my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{'CurrentUser'}->id );
unless ($status) {
Explanation => $msg,
MIMEObj => $args{'Message'}
);
- return ( 0, "Ticket not taken" );
+ return ( 0, "Ticket not taken, by email From: $From" );
}
} elsif ( $args{'Action'} =~ /^resolve$/i ) {
my $new_status = $args{'Ticket'}->FirstInactiveStatus;
Explanation => $msg,
MIMEObj => $args{'Message'}
);
- return ( 0, "Ticket not resolved" );
+ return ( 0, "Ticket not resolved, by email From: $From" );
}
}
} else {
- return ( 0, "Not supported unsafe action $args{'Action'}", $args{'Ticket'} );
+ return ( 0, "Not supported unsafe action $args{'Action'}, by email From: $From", $args{'Ticket'} );
}
return ( 1, "Success" );
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
# END BPS TAGGED BLOCK }}}
package RT::Interface::REST;
+use LWP::MediaTypes qw(guess_media_type);
use strict;
use warnings;
use RT;
use base 'Exporter';
-our @EXPORT = qw(expand_list form_parse form_compose vpush vsplit);
+our @EXPORT = qw(expand_list form_parse form_compose vpush vsplit process_attachments);
sub custom_field_spec {
my $self = shift;
return \@words;
}
+sub process_attachments {
+ my $entity = shift;
+ my @list = @_;
+ return 1 unless @list;
+
+ my $m = $HTML::Mason::Commands::m;
+ my $cgi = $m->cgi_object;
+
+ my $i = 1;
+ foreach my $e ( @list ) {
+
+ my $fh = $cgi->upload("attachment_$i");
+ return (0, "No attachment for $e") unless $fh;
+
+ local $/=undef;
+
+ my $file = $e;
+ $file =~ s#^.*[\\/]##;
+
+ my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
+
+ my $buf;
+ while (sysread($fh, $buf, 8192)) {
+ syswrite($tmp_fh, $buf);
+ }
+
+ my $info = $cgi->uploadInfo($fh);
+ my $new_entity = $entity->attach(
+ Path => $tmp_fn,
+ Type => $info->{'Content-Type'} || guess_media_type($tmp_fn),
+ Filename => $file,
+ Disposition => "attachment",
+ );
+ $new_entity->bodyhandle->{'_dirty_hack_to_save_a_ref_tmp_fh'} = $tmp_fh;
+ $i++;
+ }
+ return (1);
+}
+
RT::Base->_ImportOverlays();
1;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
# While these can be used for denial-of-service against RT
# (construct a very inefficient query and trick lots of users into
# running them against RT) it's incredibly useful to be able to link
- # to a search result or bookmark a result page.
+ # to a search result (or chart) or bookmark a result page.
'/Search/Results.html' => 1,
'/Search/Simple.html' => 1,
- '/m/tickets/search' => 1,
+ '/m/tickets/search' => 1,
+ '/Search/Chart.html' => 1,
+
+ # This page takes Attachment and Transaction argument to figure
+ # out what to show, but it's read only and will deny information if you
+ # don't have ShowOutgoingEmail.
+ '/Ticket/ShowEmailRecord.html' => 1,
);
# Components which are blacklisted from automatic, argument-based whitelisting.
$RT::Logger->error("Couldn't make multipart message")
if !$rv || $rv !~ /^(?:DONE|ALREADY)$/;
- foreach ( values %{ $ARGS{'Attachments'} } ) {
+ foreach ( map $ARGS{Attachments}->{$_}, sort keys %{ $ARGS{'Attachments'} } ) {
unless ($_) {
$RT::Logger->error("Couldn't add empty attachemnt");
next;
if ( $args{ARGSRef}->{'UpdateAttachments'} ) {
$Message->make_multipart;
- $Message->add_part($_) foreach values %{ $args{ARGSRef}->{'UpdateAttachments'} };
+ $Message->add_part($_) foreach map $args{ARGSRef}->{UpdateAttachments}{$_},
+ sort keys %{ $args{ARGSRef}->{'UpdateAttachments'} };
}
if ( $args{ARGSRef}->{'AttachTickets'} ) {
while ( my $reminder = $reminder_collection->Next ) {
my $resolve_status = $reminder->QueueObj->Lifecycle->ReminderStatusOnResolve;
if ( $reminder->Status ne $resolve_status && $args->{ 'Complete-Reminder-' . $reminder->id } ) {
- $Ticket->Reminders->Resolve($reminder);
+ my ($status, $msg) = $Ticket->Reminders->Resolve($reminder);
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
+
}
elsif ( $reminder->Status eq $resolve_status && !$args->{ 'Complete-Reminder-' . $reminder->id } ) {
- $Ticket->Reminders->Open($reminder);
+ my ($status, $msg) = $Ticket->Reminders->Open($reminder);
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
}
if ( exists( $args->{ 'Reminder-Subject-' . $reminder->id } ) && ( $reminder->Subject ne $args->{ 'Reminder-Subject-' . $reminder->id } )) {
- $reminder->SetSubject( $args->{ 'Reminder-Subject-' . $reminder->id } ) ;
+ my ($status, $msg) = $reminder->SetSubject( $args->{ 'Reminder-Subject-' . $reminder->id } ) ;
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
}
if ( exists( $args->{ 'Reminder-Owner-' . $reminder->id } ) && ( $reminder->Owner != $args->{ 'Reminder-Owner-' . $reminder->id } )) {
- $reminder->SetOwner( $args->{ 'Reminder-Owner-' . $reminder->id } , "Force" ) ;
+ my ($status, $msg) = $reminder->SetOwner( $args->{ 'Reminder-Owner-' . $reminder->id } , "Force" ) ;
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
}
if ( exists( $args->{ 'Reminder-Due-' . $reminder->id } ) && $args->{ 'Reminder-Due-' . $reminder->id } ne '' ) {
Value => $args->{ 'Reminder-Due-' . $reminder->id }
);
if ( defined $DateObj->Unix && $DateObj->Unix != $reminder->DueObj->Unix ) {
- $reminder->SetDue( $DateObj->ISO );
+ my ($status, $msg) = $reminder->SetDue( $DateObj->ISO );
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
}
}
}
}
# complex things
- elsif ( my ( $mainkey, $subkey ) = $args{'Name'} =~ /^(.*?)\.{(.+)}$/ ) {
+ elsif ( my ( $mainkey, $subkey ) = $args{'Name'} =~ /^(.*?)\.(.+)$/ ) {
+ $subkey =~ s/^\{(.*)\}$/$1/;
return undef unless $args{'Map'}->{$mainkey};
return $args{'Map'}{$mainkey}{ $args{'Attribute'} }
unless ref $args{'Map'}{$mainkey}{ $args{'Attribute'} } eq 'CODE';
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
die "couldn't execute query: ". $dbh->errstr unless defined $rows;
}
- $RT::Logger->info("successfuly deleted $rows sessions");
+ $RT::Logger->info("successfully deleted $rows sessions");
return;
}
next;
}
tied(%session)->delete;
- $RT::Logger->info("successfuly deleted session '$id'");
+ $RT::Logger->info("successfully deleted session '$id'");
}
+ # Apache::Session::Lock::File will clean out locks older than X, but it
+ # leaves around bogus locks if they're too new, even though they're
+ # guaranteed dead. On even just largeish installs, the accumulated number
+ # of them may bump into ext3/4 filesystem limits since Apache::Session
+ # doesn't use a fan-out tree.
my $lock = Apache::Session::Lock::File->new;
$lock->clean( $dir, $older_than );
+ # Take matters into our own hands and clear bogus locks hanging around
+ # regardless of how recent they are.
+ $self->ClearOrphanLockFiles($dir);
+
return;
}
+=head3 ClearOrphanLockFiles
+
+Takes a directory in which to look for L<Apache::Session::Lock::File> locks
+which no longer have a corresponding session file. If not provided, the
+directory is taken from the session configuration data.
+
+=cut
+
+sub ClearOrphanLockFiles {
+ my $class = shift;
+ my $dir = shift || $class->Attributes->{Directory}
+ or return;
+
+ if (opendir my $dh, $dir) {
+ for (readdir $dh) {
+ next unless /^Apache-Session-([0-9a-f]{32})\.lock$/;
+ next if -e "$dir/$1";
+
+ RT->Logger->debug("deleting orphaned session lockfile '$_'");
+
+ unlink "$dir/$_"
+ or warn "Failed to unlink session lockfile $dir/$_: $!";
+ }
+ closedir $dh;
+ } else {
+ warn "Unable to open directory '$dir' for reading: $!";
+ }
+}
+
=head3 ClearByUser
Checks all sessions and if user has more then one session
my $class = $self->Class;
my $attrs = $self->Attributes;
+ my $deleted;
my %seen = ();
foreach my $id( @{ $self->Ids } ) {
my %session;
}
}
tied(%session)->delete;
- $RT::Logger->info("successfuly deleted session '$id'");
+ $RT::Logger->info("successfully deleted session '$id'");
+ $deleted++;
}
+ $self->ClearOrphanLockFiles if $deleted;
}
sub TIEHASH {
eval { tie %session, $class, $id, $attrs };
eval { tie %session, $class, undef, $attrs } if $@;
if ( $@ ) {
- die loc("RT couldn't store your session.") . "\n"
- . loc("This may mean that that the directory '[_1]' isn't writable or a database table is missing or corrupt.",
- $RT::MasonSessionDir)
- . "\n\n"
+ die "RT couldn't store your session. "
+ . "This may mean that that the directory '$RT::MasonSessionDir' isn't writable or a database table is missing or corrupt.\n\n"
. $@;
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
}
my %seen;
- @statuses = grep !$seen{ $_ }++, @statuses;
+ @statuses = grep !$seen{ lc $_ }++, @statuses;
$lifecycle->{''} = \@statuses;
unless ( $lifecycle->{'transitions'}{''} ) {
- $lifecycle->{'transitions'}{''} = [ grep $_ ne 'deleted', @statuses ];
+ $lifecycle->{'transitions'}{''} = [ grep lc $_ ne 'deleted', @statuses ];
}
my @actions;
foreach my $type ( qw(initial active inactive), '' ) {
my %seen;
- @{ $all{ $type } } = grep !$seen{ $_ }++, @{ $all{ $type } };
+ @{ $all{ $type } } = grep !$seen{ lc $_ }++, @{ $all{ $type } };
push @{ $all{''} }, @{ $all{ $type } } if $type;
}
$LIFECYCLES_CACHE{''} = \%all;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
use HTML::Entities qw//;
+__PACKAGE__->_accessorize(
+ "batch"
+);
+
sub new {
my $self = shift->SUPER::new(@_);
$self->index(1);
my $self = shift;
my ($name, $section) = @_;
+ $name .= ""; # stringify name, it may be an object
+
$section = defined $section
? '#' . $self->idify($section, 1)
: '';
my $local;
- if ($name =~ /^RT::/) {
+ if ($name =~ /^RT(::(?!Extension::|Authen::)|$)/ or $self->batch->found($name)) {
$local = join "/",
map { $self->encode_entities($_) }
split /::/, $name;
}
- elsif ($name =~ /^rt[-_]/) {
+ elsif ($name =~ /^rt([-_]|$)/) {
$local = $self->encode_entities($name);
}
- elsif ($name eq "RT_Config" or $name eq "RT_Config.pm") {
- $local = "RT_Config";
+ elsif ($name =~ /^(\w+)_Config(\.pm)?$/) {
+ $name = "$1_Config";
+ $local = "$1_Config";
+ }
+ elsif ($name eq 'README') {
+ # We process README separately in devel/tools/rt-static-docs
+ $local = $name;
}
# These matches handle links that look like filenames, such as those we
# parse out of F<> tags.
elsif ( $name =~ m{^(?:lib/)(RT/[\w/]+?)\.pm$}
or $name =~ m{^(?:docs/)(.+?)\.pod$})
{
+ $name = join "::", split '/', $1;
$local = join "/",
map { $self->encode_entities($_) }
split /\//, $1;
if ($local) {
# Resolve links correctly by going up
- my $depth = $self->batch_mode_current_level - 1;
- return ($depth ? "../" x $depth : "") . "$local.html$section";
+ my $found = $self->batch->found($name);
+ my $depth = $self->batch_mode_current_level
+ + ($found ? -1 : 1);
+ return ($depth ? "../" x $depth : "") . ($found ? "" : "rt/latest/") . "$local.html$section";
} else {
return;
}
}
+sub batch_mode_page_object_init {
+ my ($self, $batch, $module, $infile, $outfile, $depth) = @_;
+ $self->SUPER::batch_mode_page_object_init(@_[1..$#_]);
+ $self->batch( $batch );
+ return $self;
+}
+
1;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
my %page = @_;
local $_ = $page{name};
return 1 if /^(README|UPGRADING)/;
- return 1 if $_ eq "RT_Config";
+ return 1 if /^RT\w*?_Config$/;
return 1 if $_ eq "web_deployment";
return 1 if $page{infile} =~ m{^configure(\.ac)?$};
return 0;
Pod::Simple::HTMLBatch::esc(@_);
}
+sub found {
+ my ($self, $module) = @_;
+ return grep { $_->[0] eq $module } @{$self->_contents};
+}
+
1;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
return 1;
}
- $args{'Right'} = RT::ACE->CanonicalizeRightName( $args{'Right'} );
- unless ( $args{'Right'} ) {
+ if ( my $right = RT::ACE->CanonicalizeRightName( $args{'Right'} ) ) {
+ $args{'Right'} = $right;
+ } else {
$RT::Logger->error(
"Invalid right. Couldn't canonicalize right '$args{'Right'}'");
return undef;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
sub _Init {
my $self = shift;
+ $self->{'with_disabled_column'} = 1;
return ( $self->SUPER::_Init(@_) );
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
}
-=head2 _EncodeLOB BODY MIME_TYPE
+=head2 _EncodeLOB BODY MIME_TYPE FILENAME
-Takes a potentially large attachment. Returns (ContentEncoding, EncodedBody) based on system configuration and selected database
+Takes a potentially large attachment. Returns (ContentEncoding,
+EncodedBody, MimeType, Filename) based on system configuration and
+selected database. Returns a custom (short) text/plain message if
+DropLongAttachments causes an attachment to not be stored.
+
+Encodes your data as base64 or Quoted-Printable as needed based on your
+Databases's restrictions and the UTF-8ness of the data being passed in. Since
+we are storing in columns marked UTF8, we must ensure that binary data is
+encoded on databases which are strict.
+
+This function expects to receive an octet string in order to properly
+evaluate and encode it. It will return an octet string.
=cut
$MaxSize = $MaxSize * 3 / 4;
# Some databases (postgres) can't handle non-utf8 data
} elsif ( !$RT::Handle->BinarySafeBLOBs
- && $MIMEType !~ /text\/plain/gi
+ && $Body =~ /\P{ASCII}/
&& !Encode::is_utf8( $Body, 1 ) ) {
$ContentEncoding = 'quoted-printable';
}
. length($Body));
$RT::Logger->info( "It started: " . substr( $Body, 0, 60 ) );
$Filename .= ".txt" if $Filename;
- return ("none", "Large attachment dropped", "plain/text", $Filename );
+ return ("none", "Large attachment dropped", "text/plain", $Filename );
}
}
}
+=head2 _DecodeLOB
+
+Unpacks data stored in the database, which may be base64 or QP encoded
+because of our need to store binary and badly encoded data in columns
+marked as UTF-8. Databases such as PostgreSQL and Oracle care that you
+are feeding them invalid UTF-8 and will refuse the content. This
+function handles unpacking the encoded data.
+
+It returns textual data as a UTF-8 string which has been processed by Encode's
+PERLQQ filter which will replace the invalid bytes with \x{HH} so you can see
+the invalid byte but won't run into problems treating the data as UTF-8 later.
+
+This is similar to how we filter all data coming in via the web UI in
+RT::Interface::Web::DecodeARGS. This filter should only end up being
+applied to old data from less UTF-8-safe versions of RT.
+
+Important Note - This function expects an octet string and returns a
+character string for non-binary data.
+
+=cut
+
sub _DecodeLOB {
my $self = shift;
my $ContentType = shift || '';
return ( $self->loc( "Unknown ContentEncoding [_1]", $ContentEncoding ) );
}
if ( RT::I18N::IsTextualContentType($ContentType) ) {
- $Content = Encode::decode_utf8($Content) unless Encode::is_utf8($Content);
+ $Content = Encode::decode('UTF-8',$Content,Encode::FB_PERLQQ) unless Encode::is_utf8($Content);
}
return ($Content);
}
if ( $args{'Base'} and $args{'Target'} ) {
$RT::Logger->debug( "$self tried to create a link. both base and target were specified" );
- return ( 0, $self->loc("Can't specifiy both base and target") );
+ return ( 0, $self->loc("Can't specify both base and target") );
}
elsif ( $args{'Base'} ) {
$args{'Target'} = $self->URI();
if ( $args{'Base'} and $args{'Target'} ) {
$RT::Logger->debug("$self ->_DeleteLink. got both Base and Target");
- return ( 0, $self->loc("Can't specifiy both base and target") );
+ return ( 0, $self->loc("Can't specify both base and target") );
}
elsif ( $args{'Base'} ) {
$args{'Target'} = $self->URI();
sub CustomFieldLookupType {
my $self = shift;
- return ref($self);
+ return ref($self) || $self;
}
my $is_the_same = 1;
if ( defined $args{'Value'} ) {
$is_the_same = 0 unless defined $old_content
- && lc $old_content eq lc $args{'Value'};
+ && $old_content eq $args{'Value'};
} else {
$is_the_same = 0 if defined $old_content;
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
return ( 0, $self->loc( "Failed to load ticket [_1]", $self->Ticket ) );
}
- if ( $ticket->Status eq 'deleted' ) {
+ if ( lc $ticket->Status eq 'deleted' ) {
return ( 0, $self->loc("Can't link to a deleted ticket") );
}
RefersTo => $self->Ticket,
Type => 'reminder',
Queue => $self->TicketObj->Queue,
+ Status => $self->TicketObj->QueueObj->Lifecycle->ReminderStatusOnOpen,
);
$self->TicketObj->_NewTransaction(
Type => 'AddReminder',
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
sub Label {
my $self = shift;
my $field = shift;
- if ( $field =~ /^(?:CF|CustomField)\.{(.*)}$/ ) {
+ if ( $field =~ /^(?:CF|CustomField)\.\{(.*)\}$/ ) {
my $cf = $1;
return $self->CurrentUser->loc( "Custom field '[_1]'", $cf ) if $cf =~ /\D/;
my $obj = RT::CustomField->new( $self->CurrentUser );
$func = "SUBSTR($func,1,4)";
}
$args{'FUNCTION'} = $func;
- } elsif ( $field =~ /^(?:CF|CustomField)\.{(.*)}$/ ) { #XXX: use CFDecipher method
+ } elsif ( $field =~ /^(?:CF|CustomField)\.\{(.*)\}$/ ) { #XXX: use CFDecipher method
my $cf_name = $1;
my $cf = RT::CustomField->new( $self->CurrentUser );
$cf->Load($cf_name);
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
[ 10 => sub { return "subject" if $_[1] } ],
[ 20 => sub { return "id" if /^#?\d+$/ } ],
[ 30 => sub { return "requestor" if /\w+@\w+/} ],
+ [ 35 => sub { return "domain" if /^@\w+/} ],
[ 40 => sub {
return "status" if RT::Queue->new( $_[2] )->IsValidStatus( $_ )
}],
return watcher => (!$_[2] and $_[1] eq "me") ? "Watcher.id = '__CurrentUser__'" : "Watcher = '$_[1]'";
}
sub HandleRequestor { return requestor => "Requestor STARTSWITH '$_[1]'"; }
+sub HandleDomain { $_[1] =~ s/^@?/@/; return requestor => "Requestor ENDSWITH '$_[1]'"; }
sub HandleQueue { return queue => "Queue = '$_[1]'"; }
sub HandleQ { return queue => "Queue = '$_[1]'"; }
sub HandleCf { return "cf.$_[3]" => "'CF.{$_[3]}' LIKE '$_[1]'"; }
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
$self->SUPER::_Init( 'Handle' => $RT::Handle);
}
+sub _Handle { return $RT::Handle }
+
sub CleanSlate {
my $self = shift;
$self->{'_sql_aliases'} = {};
+ delete $self->{'handled_disabled_column'};
+ delete $self->{'find_disabled_rows'};
return $self->SUPER::CleanSlate(@_);
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
Be sure to specify an absolute path.
+=head1 Database Indexes
+
+We have found that the following indexes significantly speed up
+shredding on most databases.
+
+ CREATE INDEX SHREDDER_CGM1 ON CachedGroupMembers(MemberId, GroupId, Disabled);
+ CREATE INDEX SHREDDER_CGM2 ON CachedGroupMembers(ImmediateParentId,MemberId);
+ CREATE INDEX SHREDDER_CGM3 on CachedGroupMembers (Via, Id);
+
+ CREATE UNIQUE INDEX SHREDDER_GM1 ON GroupMembers(MemberId, GroupId);
+
+ CREATE INDEX SHREDDER_TXN1 ON Transactions(ReferenceType, OldReference);
+ CREATE INDEX SHREDDER_TXN2 ON Transactions(ReferenceType, NewReference);
+ CREATE INDEX SHREDDER_TXN3 ON Transactions(Type, OldValue);
+ CREATE INDEX SHREDDER_TXN4 ON Transactions(Type, NewValue)
=head1 INFORMATION FOR DEVELOPERS
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
$args{'Queue'} = $QueueObj->Id;
}
- my $result = $self->SUPER::Create(
+ my ( $result, $msg ) = $self->SUPER::Create(
Content => $args{'Content'},
Queue => $args{'Queue'},
Description => $args{'Description'},
Type => $args{'Type'},
);
- return ($result);
+ if ( wantarray ) {
+ return ( $result, $msg );
+ } else {
+ return ( $result );
+ }
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
use strict;
use warnings;
+BEGIN { $^W = 1 };
use base 'Test::More';
$acl->Limit( FIELD => 'RightName', OPERATOR => '!=', VALUE => 'SuperUser' );
while ( my $ace = $acl->Next ) {
my $obj = $ace->PrincipalObj->Object;
- if ( $obj->isa('RT::Group') && $obj->Type eq 'UserEquiv' && $obj->Instance == RT->Nobody->id ) {
+ if ( $obj->isa('RT::Group') && ($obj->Type||'') eq 'UserEquiv' && $obj->Instance == RT->Nobody->id ) {
next;
}
$ace->Delete;
my $in_end = shift;
return unless @SERVERS;
- my $sig = 'TERM';
- $sig = 'INT' if $ENV{'RT_TEST_WEB_HANDLER'} eq "plack";
- kill $sig, @SERVERS;
+ kill 'TERM', @SERVERS;
foreach my $pid (@SERVERS) {
if ($ENV{RT_TEST_WEB_HANDLER} =~ /^apache/) {
sleep 1 while kill 0, $pid;
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
}
}
- if ( $obj && $obj->Status eq 'deleted' ) {
+ if ( $obj && lc $obj->Status eq 'deleted' ) {
push @non_fatal_errors,
$self->loc("Linking. Can't link to a deleted ticket");
next;
}
return ( 0, "Can't link to a deleted ticket" )
- if $other_ticket && $other_ticket->Status eq 'deleted';
+ if $other_ticket && lc $other_ticket->Status eq 'deleted';
return $self->_AddLink(%args);
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
QueueCc => [ 'WATCHERFIELD' => 'Cc' => 'Queue', ], #loc_left_pair
QueueAdminCc => [ 'WATCHERFIELD' => 'AdminCc' => 'Queue', ], #loc_left_pair
QueueWatcher => [ 'WATCHERFIELD' => undef => 'Queue', ], #loc_left_pair
- CustomFieldValue => [ 'CUSTOMFIELD', ], #loc_left_pair
- CustomField => [ 'CUSTOMFIELD', ], #loc_left_pair
- CF => [ 'CUSTOMFIELD', ], #loc_left_pair
+ CustomFieldValue => [ 'CUSTOMFIELD' => 'Ticket' ], #loc_left_pair
+ CustomField => [ 'CUSTOMFIELD' => 'Ticket' ], #loc_left_pair
+ CF => [ 'CUSTOMFIELD' => 'Ticket' ], #loc_left_pair
Updated => [ 'TRANSDATE', ], #loc_left_pair
RequestorGroup => [ 'MEMBERSHIPFIELD' => 'Requestor', ], #loc_left_pair
CCGroup => [ 'MEMBERSHIPFIELD' => 'Cc', ], #loc_left_pair
my $is_null = 0;
$is_null = 1 if !$value || $value =~ /^null$/io;
- unless ($is_null) {
- $value = RT::URI->new( $sb->CurrentUser )->CanonicalizeURI( $value );
- }
-
my $direction = $meta->[1] || '';
my ($matchfield, $linkfield) = ('', '');
if ( $direction eq 'To' ) {
$op = ($op =~ /^(=|IS)$/i)? 'IS': 'IS NOT';
}
elsif ( $value =~ /\D/ ) {
+ $value = RT::URI->new( $sb->CurrentUser )->CanonicalizeURI( $value );
$is_local = 0;
}
$matchfield = "Local$matchfield" if $is_local;
}
$rest{SUBKEY} ||= 'EmailAddress';
- my $groups = $self->_RoleGroupsJoin( Type => $type, Class => $class, New => !$type );
+ my ($groups, $group_members, $users);
+ if ( $rest{'BUNDLE'} ) {
+ ($groups, $group_members, $users) = @{ $rest{'BUNDLE'} };
+ } else {
+ $groups = $self->_RoleGroupsJoin( Type => $type, Class => $class, New => !$type );
+ }
$self->_OpenParen;
if ( $op =~ /^IS(?: NOT)?$/i ) {
# is [not] empty case
- my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
+ $group_members ||= $self->_GroupMembersJoin( GroupsAlias => $groups );
# to avoid joining the table Users into the query, we just join GM
# and make sure we don't match records where group is member of itself
$self->SUPER::Limit(
$users_obj->RowsPerPage(2);
my @users = @{ $users_obj->ItemsArrayRef };
- my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups );
+ $group_members ||= $self->_GroupMembersJoin( GroupsAlias => $groups );
if ( @users <= 1 ) {
my $uid = 0;
$uid = $users[0]->id if @users;
VALUE => "$group_members.MemberId",
QUOTEVALUE => 0,
);
- my $users = $self->Join(
+ $users ||= $self->Join(
TYPE => 'LEFT',
ALIAS1 => $group_members,
FIELD1 => 'MemberId',
} else {
# positive condition case
- my $group_members = $self->_GroupMembersJoin(
+ $group_members ||= $self->_GroupMembersJoin(
GroupsAlias => $groups, New => 1, Left => 0
);
- my $users = $self->Join(
+ $users ||= $self->Join(
TYPE => 'LEFT',
ALIAS1 => $group_members,
FIELD1 => 'MemberId',
);
}
$self->_CloseParen;
+ return ($groups, $group_members, $users);
}
sub _RoleGroupsJoin {
Try and turn a CF descriptor into (cfid, cfname) object pair.
+Takes an optional second parameter of the CF LookupType, defaults to Ticket CFs.
+
=cut
sub _CustomFieldDecipher {
- my ($self, $string) = @_;
+ my ($self, $string, $lookuptype) = @_;
+ $lookuptype ||= $self->_SingularClass->CustomFieldLookupType;
- my ($queue, $field, $column) = ($string =~ /^(?:(.+?)\.)?{(.+)}(?:\.(Content|LargeContent))?$/);
+ my ($object, $field, $column) = ($string =~ /^(?:(.+?)\.)?\{(.+)\}(?:\.(Content|LargeContent))?$/);
$field ||= ($string =~ /^{(.*?)}$/)[0] || $string;
- my $cf;
- if ( $queue ) {
- my $q = RT::Queue->new( $self->CurrentUser );
- $q->Load( $queue );
+ my ($cf, $applied_to);
+
+ if ( $object ) {
+ my $record_class = RT::CustomField->RecordClassFromLookupType($lookuptype);
+ $applied_to = $record_class->new( $self->CurrentUser );
+ $applied_to->Load( $object );
- if ( $q->id ) {
- # $queue = $q->Name; # should we normalize the queue?
- $cf = $q->CustomField( $field );
+ if ( $applied_to->id ) {
+ RT->Logger->debug("Limiting to CFs identified by '$field' applied to $record_class #@{[$applied_to->id]} (loaded via '$object')");
}
else {
- $RT::Logger->warning("Queue '$queue' doesn't exist, parsed from '$string'");
- $queue = 0;
+ RT->Logger->warning("$record_class '$object' doesn't exist, parsed from '$string'");
+ $object = 0;
+ undef $applied_to;
}
}
- elsif ( $field =~ /\D/ ) {
- $queue = '';
+
+ if ( $field =~ /\D/ ) {
+ $object ||= '';
my $cfs = RT::CustomFields->new( $self->CurrentUser );
- $cfs->Limit( FIELD => 'Name', VALUE => $field );
- $cfs->LimitToLookupType('RT::Queue-RT::Ticket');
+ $cfs->Limit( FIELD => 'Name', VALUE => $field, ($applied_to ? (CASESENSITIVE => 0) : ()) );
+ $cfs->LimitToLookupType($lookuptype);
+
+ if ($applied_to) {
+ $cfs->SetContextObject($applied_to);
+ $cfs->LimitToObjectId($applied_to->id);
+ }
# if there is more then one field the current user can
# see with the same name then we shouldn't return cf object
else {
$cf = RT::CustomField->new( $self->CurrentUser );
$cf->Load( $field );
+ $cf->SetContextObject($applied_to)
+ if $cf->id and $applied_to;
}
- return ($queue, $field, $cf, $column);
+ return ($object, $field, $cf, $column);
}
=head2 _CustomFieldJoin
=cut
+our %JOIN_ALIAS_FOR_LOOKUP_TYPE = (
+ RT::Ticket->CustomFieldLookupType => sub { "main" },
+);
+
sub _CustomFieldJoin {
- my ($self, $cfkey, $cfid, $field) = @_;
+ my ($self, $cfkey, $cfid, $field, $type) = @_;
+ $type ||= RT::Ticket->CustomFieldLookupType;
+
# Perform one Join per CustomField
if ( $self->{_sql_object_cfv_alias}{$cfkey} ||
$self->{_sql_cf_alias}{$cfkey} )
$self->{_sql_cf_alias}{$cfkey} );
}
- my ($TicketCFs, $CFs);
+ my $ObjectAlias = $JOIN_ALIAS_FOR_LOOKUP_TYPE{$type}
+ ? $JOIN_ALIAS_FOR_LOOKUP_TYPE{$type}->($self)
+ : die "We don't know how to join on $type";
+
+ my ($ObjectCFs, $CFs);
if ( $cfid ) {
- $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+ $ObjectCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
TYPE => 'LEFT',
- ALIAS1 => 'main',
+ ALIAS1 => $ObjectAlias,
FIELD1 => 'id',
TABLE2 => 'ObjectCustomFieldValues',
FIELD2 => 'ObjectId',
);
$self->SUPER::Limit(
- LEFTJOIN => $TicketCFs,
+ LEFTJOIN => $ObjectCFs,
FIELD => 'CustomField',
VALUE => $cfid,
ENTRYAGGREGATOR => 'AND'
LEFTJOIN => $CFs,
ENTRYAGGREGATOR => 'AND',
FIELD => 'LookupType',
- VALUE => 'RT::Queue-RT::Ticket',
+ VALUE => $type,
);
$self->SUPER::Limit(
LEFTJOIN => $CFs,
VALUE => $field,
);
- $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+ $ObjectCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
TYPE => 'LEFT',
ALIAS1 => $CFs,
FIELD1 => 'id',
FIELD2 => 'CustomField',
);
$self->SUPER::Limit(
- LEFTJOIN => $TicketCFs,
+ LEFTJOIN => $ObjectCFs,
FIELD => 'ObjectId',
- VALUE => 'main.id',
+ VALUE => "$ObjectAlias.id",
QUOTEVALUE => 0,
ENTRYAGGREGATOR => 'AND',
);
}
+
$self->SUPER::Limit(
- LEFTJOIN => $TicketCFs,
+ LEFTJOIN => $ObjectCFs,
FIELD => 'ObjectType',
- VALUE => 'RT::Ticket',
+ VALUE => RT::CustomField->ObjectTypeFromLookupType($type),
ENTRYAGGREGATOR => 'AND'
);
$self->SUPER::Limit(
- LEFTJOIN => $TicketCFs,
+ LEFTJOIN => $ObjectCFs,
FIELD => 'Disabled',
OPERATOR => '=',
VALUE => '0',
ENTRYAGGREGATOR => 'AND'
);
- return ($TicketCFs, $CFs);
+ return ($ObjectCFs, $CFs);
}
=head2 _CustomFieldLimit
sub _CustomFieldLimit {
my ( $self, $_field, $op, $value, %rest ) = @_;
+ my $meta = $FIELD_METADATA{ $_field };
+ my $class = $meta->[1] || 'Ticket';
+ my $type = "RT::$class"->CustomFieldLookupType;
+
my $field = $rest{'SUBKEY'} || die "No field specified";
# For our sanity, we can only limit on one queue at a time
- my ($queue, $cfid, $cf, $column);
- ($queue, $field, $cf, $column) = $self->_CustomFieldDecipher( $field );
+ my ($object, $cfid, $cf, $column);
+ ($object, $field, $cf, $column) = $self->_CustomFieldDecipher( $field, $type );
$cfid = $cf ? $cf->id : 0 ;
# If we're trying to find custom fields that don't match something, we
my $single_value = !$cf || !$cfid || $cf->SingleValue;
- my $cfkey = $cfid ? $cfid : "$queue.$field";
+ my $cfkey = $cfid ? $cfid : "$type-$object.$field";
if ( $null_op && !$column ) {
# IS[ NOT] NULL without column is the same as has[ no] any CF value,
# we can reuse our default joins for this operation
# with column specified we have different situation
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type );
$self->_OpenParen;
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'id',
OPERATOR => $op,
VALUE => $value,
$self->_OpenParen;
if ( $op !~ /NOT|!=|<>/i ) { # positive equation
$self->_CustomFieldLimit(
- 'CF', '<=', $end_ip, %rest,
+ $_field, '<=', $end_ip, %rest,
SUBKEY => $rest{'SUBKEY'}. '.Content',
);
$self->_CustomFieldLimit(
- 'CF', '>=', $start_ip, %rest,
+ $_field, '>=', $start_ip, %rest,
SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
ENTRYAGGREGATOR => 'AND',
);
# estimations and scan less rows
# have to disable this tweak because of ipv6
# $self->_CustomFieldLimit(
-# $field, '>=', '000.000.000.000', %rest,
+# $_field, '>=', '000.000.000.000', %rest,
# SUBKEY => $rest{'SUBKEY'}. '.Content',
# ENTRYAGGREGATOR => 'AND',
# );
# $self->_CustomFieldLimit(
-# $field, '<=', '255.255.255.255', %rest,
+# $_field, '<=', '255.255.255.255', %rest,
# SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
# ENTRYAGGREGATOR => 'AND',
# );
}
else { # negative equation
- $self->_CustomFieldLimit($field, '>', $end_ip, %rest);
+ $self->_CustomFieldLimit($_field, '>', $end_ip, %rest);
$self->_CustomFieldLimit(
- $field, '<', $start_ip, %rest,
+ $_field, '<', $start_ip, %rest,
SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
ENTRYAGGREGATOR => 'OR',
);
}
elsif ( !$negative_op || $single_value ) {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if !$single_value && !$range_op;
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type );
$self->_OpenParen;
# otherwise search in Content and in LargeContent
if ( $column ) {
$self->_SQLLimit( $fix_op->(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => $column,
OPERATOR => $op,
VALUE => $value,
$self->_OpenParen;
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'Content',
OPERATOR => ">=",
VALUE => $daystart,
);
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'Content',
OPERATOR => "<",
VALUE => $dayend,
elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
if ( length( Encode::encode_utf8($value) ) < 256 ) {
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
else {
$self->_OpenParen;
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'Content',
OPERATOR => '=',
VALUE => '',
ENTRYAGGREGATOR => 'OR'
);
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
);
$self->_CloseParen;
$self->_SQLLimit( $fix_op->(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'LargeContent',
OPERATOR => $op,
VALUE => $value,
}
else {
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
$self->_OpenParen;
$self->_OpenParen;
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'Content',
OPERATOR => '=',
VALUE => '',
ENTRYAGGREGATOR => 'OR'
);
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
);
$self->_CloseParen;
$self->_SQLLimit( $fix_op->(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'LargeContent',
OPERATOR => $op,
VALUE => $value,
if ($negative_op) {
$self->_SQLLimit(
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => $column || 'Content',
OPERATOR => 'IS',
VALUE => 'NULL',
}
else {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+ my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type );
# reverse operation
$op =~ s/!|NOT\s+//i;
# otherwise search in Content and in LargeContent
if ( $column ) {
$self->SUPER::Limit( $fix_op->(
- LEFTJOIN => $TicketCFs,
- ALIAS => $TicketCFs,
+ LEFTJOIN => $ObjectCFs,
+ ALIAS => $ObjectCFs,
FIELD => $column,
OPERATOR => $op,
VALUE => $value,
}
else {
$self->SUPER::Limit(
- LEFTJOIN => $TicketCFs,
- ALIAS => $TicketCFs,
+ LEFTJOIN => $ObjectCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
}
$self->_SQLLimit(
%rest,
- ALIAS => $TicketCFs,
+ ALIAS => $ObjectCFs,
FIELD => 'id',
OPERATOR => 'IS',
VALUE => 'NULL',
}
push @res, { %$row, ALIAS => $users, FIELD => $subkey };
} elsif ( defined $meta->[0] && $meta->[0] eq 'CUSTOMFIELD' ) {
- my ($queue, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey );
- my $cfkey = $cf_obj ? $cf_obj->id : "$queue.$field";
+ my ($object, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey );
+ my $cfkey = $cf_obj ? $cf_obj->id : "$object.$field";
$cfkey .= ".ordering" if !$cf_obj || ($cf_obj->MaxValues||0) != 1;
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj ?$cf_obj->id :0) , $field );
+ my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj ?$cf_obj->id :0) , $field );
# this is described in _CustomFieldLimit
$self->_SQLLimit(
ALIAS => $CFs,
}
my $CFvs = $self->Join(
TYPE => 'LEFT',
- ALIAS1 => $TicketCFs,
+ ALIAS1 => $ObjectCFs,
FIELD1 => 'CustomField',
TABLE2 => 'CustomFieldValues',
FIELD2 => 'CustomField',
LEFTJOIN => $CFvs,
FIELD => 'Name',
QUOTEVALUE => 0,
- VALUE => $TicketCFs . ".Content",
+ VALUE => $ObjectCFs . ".Content",
ENTRYAGGREGATOR => 'AND'
);
push @res, { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' };
- push @res, { %$row, ALIAS => $TicketCFs, FIELD => 'Content' };
+ push @res, { %$row, ALIAS => $ObjectCFs, FIELD => 'Content' };
} elsif ( $field eq "Custom" && $subkey eq "Ownership") {
# PAW logic is "reversed"
my $order = "ASC";
$self->Limit(
VALUE => $args{VALUE},
FIELD => "CF"
- .(defined $args{'QUEUE'}? ".{$args{'QUEUE'}}" : '' )
+ .(defined $args{'QUEUE'}? ".$args{'QUEUE'}" : '' )
.".{" . $CF->Name . "}",
OPERATOR => $args{OPERATOR},
CUSTOMFIELD => 1,
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
sub _SQLLimit {
my $self = shift;
- my %args = (@_);
+ my %args = (FIELD => '', @_);
if ($args{'FIELD'} eq 'EffectiveId' &&
(!$args{'ALIAS'} || $args{'ALIAS'} eq 'main' ) ) {
$self->{'looking_at_effective_id'} = 1;
my @bundle;
my $ea = '';
+ # Bundling of joins is implemented by dynamically tracking a parallel query
+ # tree in %sub_tree as the TicketSQL is parsed. Don't be fooled by
+ # _close_bundle(), @bundle, and %can_bundle; they are completely unused for
+ # quite a long time and removed in RT 4.2. For now they stay, a useless
+ # relic.
+ #
+ # Only positive, OR'd watcher conditions are bundled currently. Each key
+ # in %sub_tree is a watcher type (Requestor, Cc, AdminCc) or the generic
+ # "Watcher" for any watcher type. Owner is not bundled because it is
+ # denormalized into a Tickets column and doesn't need a join. AND'd
+ # conditions are not bundled since a record may have multiple watchers
+ # which independently match the conditions, thus necessitating two joins.
+ #
+ # The values of %sub_tree are arrayrefs made up of:
+ #
+ # * Open parentheses "(" pushed on by the OpenParen callback
+ # * Arrayrefs of bundled join aliases pushed on by the Condition callback
+ # * Entry aggregators (AND/OR) pushed on by the EntryAggregator callback
+ #
+ # The CloseParen callback takes care of backing off the query trees until
+ # outside of the just-closed parenthetical, thus restoring the tree state
+ # an equivalent of before the parenthetical was entered.
+ #
+ # The Condition callback handles starting a new subtree or extending an
+ # existing one, determining if bundling the current condition with any
+ # subtree is possible, and pruning any dangling entry aggregators from
+ # trees.
+ #
+
+ my %sub_tree;
+ my $depth = 0;
+
my %callback;
$callback{'OpenParen'} = sub {
$self->_close_bundle(@bundle); @bundle = ();
- $self->_OpenParen
+ $self->_OpenParen;
+ $depth++;
+ push @$_, '(' foreach values %sub_tree;
};
$callback{'CloseParen'} = sub {
$self->_close_bundle(@bundle); @bundle = ();
$self->_CloseParen;
+ $depth--;
+ foreach my $list ( values %sub_tree ) {
+ if ( $list->[-1] eq '(' ) {
+ pop @$list;
+ pop @$list if $list->[-1] =~ /^(?:AND|OR)$/i;
+ }
+ else {
+ pop @$list while $list->[-2] ne '(';
+ $list->[-1] = pop @$list;
+ }
+ }
+ };
+ $callback{'EntryAggregator'} = sub {
+ $ea = $_[0] || '';
+ push @$_, $ea foreach grep @$_ && $_->[-1] ne '(', values %sub_tree;
};
- $callback{'EntryAggregator'} = sub { $ea = $_[0] || '' };
$callback{'Condition'} = sub {
my ($key, $op, $value) = @_;
+ my ($negative_op, $null_op, $inv_op, $range_op)
+ = $self->ClassifySQLOperation( $op );
# key has dot then it's compound variant and we have subkey
my $subkey = '';
($key, $subkey) = ($1, $2) if $key =~ /^([^\.]+)\.(.+)$/;
}
else {
$self->_close_bundle(@bundle); @bundle = ();
- $sub->( $self, $key, $op, $value,
+ my @res; my $bundle_with;
+ if ( $class eq 'WATCHERFIELD' && $key ne 'Owner' && !$negative_op && (!$null_op || $subkey) ) {
+ if ( !$sub_tree{$key} ) {
+ $sub_tree{$key} = [ ('(')x$depth, \@res ];
+ } else {
+ $bundle_with = $self->_check_bundling_possibility( $string, @{ $sub_tree{$key} } );
+ if ( $sub_tree{$key}[-1] eq '(' ) {
+ push @{ $sub_tree{$key} }, \@res;
+ }
+ }
+ }
+
+ # Remove our aggregator from subtrees where our condition didn't get added
+ pop @$_ foreach grep @$_ && $_->[-1] =~ /^(?:AND|OR)$/i, values %sub_tree;
+
+ # A reference to @res may be pushed onto $sub_tree{$key} from
+ # above, and we fill it here.
+ @res = $sub->( $self, $key, $op, $value,
SUBCLAUSE => '', # don't need anymore
ENTRYAGGREGATOR => $ea,
SUBKEY => $subkey,
+ BUNDLE => $bundle_with,
);
}
$self->{_sql_looking_at}{lc $key} = 1;
$self->_close_bundle(@bundle); @bundle = ();
}
+sub _check_bundling_possibility {
+ my $self = shift;
+ my $string = shift;
+ my @list = reverse @_;
+ while (my $e = shift @list) {
+ next if $e eq '(';
+ if ( lc($e) eq 'and' ) {
+ return undef;
+ }
+ elsif ( lc($e) eq 'or' ) {
+ return shift @list;
+ }
+ else {
+ # should not happen
+ $RT::Logger->error(
+ "Joins optimization failed when parsing '$string'. It's bug in RT, contact Best Practical"
+ );
+ die "Internal error. Contact your system administrator.";
+ }
+ }
+ return undef;
+}
+
=head2 ClausesToSQL
=cut
$self->{_sql_query} = $query;
eval { $self->_parser( $query ); };
if ( $@ ) {
- $RT::Logger->error( $@ );
- return (0, $@);
+ my $error = "$@";
+ $RT::Logger->error("Couldn't parse query: $error");
+ return (0, $error);
}
# We only want to look at EffectiveId's (mostly) for these searches.
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
}
if ( $args{'Quote'} ) {
+ $content = $self->ApplyQuoteWrap(content => $content,
+ cols => $args{'Wrap'} );
- # What's the longest line like?
- my $max = 0;
- foreach ( split ( /\n/, $content ) ) {
- $max = length if length > $max;
- }
-
- if ( $max > $args{'Wrap'}+6 ) { # 76 ) {
- require Text::Wrapper;
- my $wrapper = Text::Wrapper->new(
- columns => $args{'Wrap'},
- body_start => ( $max > 70 * 3 ? ' ' : '' ),
- par_start => ''
- );
- $content = $wrapper->wrap($content);
- }
-
- $content =~ s/^/> /gm;
$content = $self->QuoteHeader . "\n$content\n\n";
}
return $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString, $self->CreatorObj->Name);
}
+=head2 ApplyQuoteWrap PARAMHASH
+
+Wrapper to calculate wrap criteria and apply quote wrapping if needed.
+
+=cut
+
+sub ApplyQuoteWrap {
+ my $self = shift;
+ my %args = @_;
+ my $content = $args{content};
+
+ # What's the longest line like?
+ my $max = 0;
+ foreach ( split ( /\n/, $args{content} ) ) {
+ $max = length if length > $max;
+ }
+
+ if ( $max > 76 ) {
+ require Text::Quoted;
+ require Text::Wrapper;
+
+ my $structure = Text::Quoted::extract($args{content});
+ $content = $self->QuoteWrap(content_ref => $structure,
+ cols => $args{cols},
+ max => $max );
+ }
+
+ $content =~ s/^/> /gm; # use regex since string might be multi-line
+ return $content;
+}
+
+=head2 QuoteWrap PARAMHASH
+
+Wrap the contents of transactions based on Wrap settings, maintaining
+the quote character from the original.
+
+=cut
+
+sub QuoteWrap {
+ my $self = shift;
+ my %args = @_;
+ my $ref = $args{content_ref};
+ my $final_string;
+
+ if ( ref $ref eq 'ARRAY' ){
+ foreach my $array (@$ref){
+ $final_string .= $self->QuoteWrap(content_ref => $array,
+ cols => $args{cols},
+ max => $args{max} );
+ }
+ }
+ elsif ( ref $ref eq 'HASH' ){
+ return $ref->{quoter} . "\n" if $ref->{empty}; # Blank line
+
+ my $col = $args{cols} - (length $ref->{quoter});
+ my $wrapper = Text::Wrapper->new( columns => $col );
+
+ # Wrap on individual lines to honor incoming line breaks
+ # Otherwise deliberate separate lines (like a list or a sig)
+ # all get combined incorrectly into single paragraphs.
+
+ my @lines = split /\n/, $ref->{text};
+ my $wrap = join '', map { $wrapper->wrap($_) } @lines;
+ my $quoter = $ref->{quoter};
+
+ # Only add the space if actually quoting
+ $quoter .= ' ' if length $quoter;
+ $wrap =~ s/^/$quoter/mg; # use regex since string might be multi-line
+
+ return $wrap;
+ }
+ else{
+ $RT::Logger->warning("Can't apply quoting with $ref");
+ return;
+ }
+ return $final_string;
+}
+
=head2 Addresses
my $self = shift;
my $field = $self->loc('CustomField');
+ my $cf;
if ( $self->Field ) {
- my $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf = RT::CustomField->new( $self->CurrentUser );
$cf->SetContextObject( $self->Object );
$cf->Load( $self->Field );
$field = $cf->Name();
my $new = $self->NewValue;
my $old = $self->OldValue;
+ if ( $cf ) {
+
+ if ( $cf->Type eq 'DateTime' ) {
+ if ($old) {
+ my $date = RT::Date->new( $self->CurrentUser );
+ $date->Set( Format => 'ISO', Value => $old );
+ $old = $date->AsString;
+ }
+
+ if ($new) {
+ my $date = RT::Date->new( $self->CurrentUser );
+ $date->Set( Format => 'ISO', Value => $new );
+ $new = $date->AsString;
+ }
+ }
+ elsif ( $cf->Type eq 'Date' ) {
+ if ($old) {
+ my $date = RT::Date->new( $self->CurrentUser );
+ $date->Set(
+ Format => 'unknown',
+ Value => $old,
+ Timezone => 'UTC',
+ );
+ $old = $date->AsString( Time => 0, Timezone => 'UTC' );
+ }
+
+ if ($new) {
+ my $date = RT::Date->new( $self->CurrentUser );
+ $date->Set(
+ Format => 'unknown',
+ Value => $new,
+ Timezone => 'UTC',
+ );
+ $new = $date->AsString( Time => 0, Timezone => 'UTC' );
+ }
+ }
+ }
+
if ( !defined($old) || $old eq '' ) {
return $self->loc("[_1] [_2] added", $field, $new);
}
else {
return $self->loc( "[_1] changed from [_2] to [_3]",
$self->loc($self->Field),
- ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" );
+ ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")),
+ ($self->NewValue? "'".$self->NewValue ."'" : $self->loc("(no value)")));
}
},
PurgeTransaction => sub {
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
if ($resolver) {
$self->{'resolver'} = $resolver;
} else {
+ RT->Logger->warning("Failed to create new resolver object for scheme '$scheme': $@")
+ if $@ !~ m{Can't locate RT/URI/\Q$scheme\E};
$self->{'resolver'} = RT::URI::base->new($self->CurrentUser);
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
# values. Instead we set values, eval code, check pid
# on failure and reset values only in our original
# process
+ my ($oldv_dbh, $oldv_rth);
my $dbh = $RT::Handle->dbh;
+ $oldv_dbh = $dbh->{'InactiveDestroy'} if $dbh;
$dbh->{'InactiveDestroy'} = 1 if $dbh;
+ $oldv_rth = $RT::Handle->{'DisconnectHandleOnDestroy'};
$RT::Handle->{'DisconnectHandleOnDestroy'} = 0;
my ($reader, $writer);
my $err = $@;
$err =~ s/^Stack:.*$//ms;
if ( $our_pid == $$ ) {
- $dbh->{'InactiveDestroy'} = 0 if $dbh;
- $RT::Handle->{'DisconnectHandleOnDestroy'} = 1;
+ $dbh->{'InactiveDestroy'} = $oldv_dbh if $dbh;
+ $RT::Handle->{'DisconnectHandleOnDestroy'} = $oldv_rth;
die "System Error: $err";
} else {
print $writer "System Error: $err";
my ($response) = $reader->getline;
warn $response if $response;
- $dbh->{'InactiveDestroy'} = 0 if $dbh;
- $RT::Handle->{'DisconnectHandleOnDestroy'} = 1;
+ $dbh->{'InactiveDestroy'} = $oldv_dbh if $dbh;
+ $RT::Handle->{'DisconnectHandleOnDestroy'} = $oldv_rth;
return $want? (@res) : $res[0];
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
use Getopt::Long;
my %opt;
-GetOptions( \%opt, "help|h" );
+GetOptions( \%opt, "help|h",
+ "limit-to-privileged|l",
+ "skip-disabled|s",
+ "all|a",
+);
if ( $opt{help} ) {
require Pod::Usage;
RT::LoadConfig();
RT::Init();
-my $LocalOnly = @ARGV ? shift(@ARGV) : 1;
-
my %RV;
my %Ignore = (
All => [
foreach my $class (@classes) {
require "RT/$class.pm";
my $objects = "RT::$class"->new( RT->SystemUser );
- $objects->{find_disabled_rows} = 1;
+ $objects->{find_disabled_rows} = 1 unless $opt{'skip-disabled'};
$objects->UnLimit;
+ $objects->LimitToPrivileged if $class eq 'Users'
+ && $opt{'limit-to-privileged'};
+ $objects->Limit(
+ FIELD => 'Domain',
+ OPERATOR => '=',
+ VALUE => 'UserDefined'
+ ) if $class eq 'Groups';
if ( $class eq 'CustomFields' ) {
$objects->OrderByCols(
$objects->OrderBy( FIELD => 'Id' );
}
- if ($LocalOnly) {
+ unless ($opt{all}) {
next if $class eq 'ACL'; # XXX - would go into infinite loop - XXX
$objects->Limit(
FIELD => 'LastUpdatedBy',
OPERATOR => '!=',
VALUE => $SystemUserId
) if $class eq 'Users';
- $objects->Limit(
- FIELD => 'Domain',
- OPERATOR => '=',
- VALUE => 'UserDefined'
- ) if $class eq 'Groups';
}
my %fields;
+OBJECT:
while ( my $obj = $objects->Next ) {
next
if $obj->can('LastUpdatedBy')
my $rv;
- # next if $obj-> # skip default names
- foreach my $field ( sort keys %fields ) {
- my $value = $obj->__Value($field);
- $rv->{$field} = $value if ( defined($value) && length($value) );
- }
- delete $rv->{Disabled} unless $rv->{Disabled};
-
- foreach my $record ( map { /ACL/ ? 'ACE' : substr( $_, 0, -1 ) }
- @classes )
- {
- foreach my $key ( map "$record$_", ( '', 'Id' ) ) {
- next unless exists $rv->{$key};
- my $id = $rv->{$key} or next;
- my $obj = "RT::$record"->new( RT->SystemUser );
- $obj->LoadByCols( Id => $id ) or next;
- $rv->{$key} = $obj->__Value('Name') || 0;
+ if ( $class ne 'ACL' ) {
+ # next if $obj-> # skip default names
+ foreach my $field ( sort keys %fields ) {
+ my $value = $obj->__Value($field);
+ $rv->{$field} = $value if ( defined($value) && length($value) );
+ }
+ delete $rv->{Disabled} unless $rv->{Disabled};
+
+ foreach my $record ( map { /ACL/ ? 'ACE' : substr( $_, 0, -1 ) }
+ @classes )
+ {
+ foreach my $key ( map "$record$_", ( '', 'Id' ) ) {
+ next unless exists $rv->{$key};
+ my $id = $rv->{$key} or next;
+ my $obj = "RT::$record"->new( RT->SystemUser );
+ $obj->LoadByCols( Id => $id ) or next;
+ $rv->{$key} = $obj->__Value('Name') || 0;
+ }
+ }
+
+ if ( $class eq 'Users' and defined $obj->Privileged ) {
+ $rv->{Privileged} = int( $obj->Privileged );
+ } elsif ( $class eq 'CustomFields' ) {
+ my $values = $obj->Values;
+ while ( my $value = $values->Next ) {
+ push @{ $rv->{Values} }, {
+ map { ( $_ => $value->__Value($_) ) }
+ qw(
+ Name Description SortOrder
+ ),
+ };
+ }
+ if ( $obj->LookupType eq 'RT::Queue-RT::Ticket' ) {
+ # XXX-TODO: unused CF's turn into global CF when importing
+ # as the sub InsertData in RT::Handle creates a global CF
+ # when no queue is specified.
+ $rv->{Queue} = [];
+ my $applies = $obj->AppliedTo;
+ while ( my $queue = $applies->Next ) {
+ push @{ $rv->{Queue} }, $queue->Name;
+ }
+ }
}
}
+ else {
+ # 1) pick the right
+ $rv->{Right} = $obj->RightName;
+
+ # 2) Pick a level: Granted on Queue, CF, CF+Queue, or Globally?
+ for ( $obj->ObjectType ) {
+ if ( /^RT::Queue$/ ) {
+ next OBJECT if $opt{'skip-disabled'} && $obj->Object->Disabled;
+ $rv->{Queue} = $obj->Object->Name;
+ }
+ elsif ( /^RT::CustomField$/ ) {
+ next OBJECT if $opt{'skip-disabled'} && $obj->Object->Disabled;
+ $rv->{CF} = $obj->Object->Name;
+ }
+ elsif ( /^RT::Group$/ ) {
+ # No support for RT::Group ACLs in RT::Handle yet.
+ next OBJECT;
+ }
+ elsif ( /^RT::System$/ ) {
+ # skip setting anything on $rv;
+ # "Specifying none of the above will get you a global right."
+ }
+ }
- if ( $class eq 'Users' and defined $obj->Privileged ) {
- $rv->{Privileged} = int( $obj->Privileged );
- } elsif ( $class eq 'CustomFields' ) {
- my $values = $obj->Values;
- while ( my $value = $values->Next ) {
- push @{ $rv->{Values} }, {
- map { ( $_ => $value->__Value($_) ) }
- qw(
- Name Description SortOrder
- ),
- };
+ # 3) Pick a Principal; User or Group or Role
+ if ( $obj->PrincipalType eq 'Group' ) {
+ next OBJECT if $opt{'skip-disabled'} && $obj->PrincipalObj->Disabled;
+ my $group = $obj->PrincipalObj->Object;
+ for ( $group->Domain ) {
+ # An internal user group
+ if ( /^SystemInternal$/ ) {
+ $rv->{GroupDomain} = $group->Domain;
+ $rv->{GroupType} = $group->Type;
+ }
+ # An individual user
+ elsif ( /^ACLEquivalence$/ ) {
+ my $member = $group->MembersObj->Next->MemberObj;
+ next OBJECT if $opt{'skip-disabled'} && $member->Disabled;
+ $rv->{UserId} = $member->Object->Name;
+ }
+ # A group you created
+ elsif ( /^UserDefined$/ ) {
+ $rv->{GroupDomain} = 'UserDefined';
+ $rv->{GroupId} = $group->Name;
+ }
+ }
+ } else {
+ $rv->{GroupType} = $obj->PrincipalType;
+ # A system-level role
+ if ( $obj->ObjectType eq 'RT::System' ) {
+ $rv->{GroupDomain} = 'RT::System-Role';
+ }
+ # A queue-level role
+ elsif ( $obj->ObjectType eq 'RT::Queue' ) {
+ $rv->{GroupDomain} = 'RT::Queue-Role';
+ }
+ }
+ if ( $obj->LookupType eq 'RT::Queue-RT::Ticket' ) {
+ # XXX-TODO: unused CF's turn into global CF when importing
+ # as the sub InsertData in RT::Handle creates a global CF
+ # when no queue is specified.
+ $rv->{Queue} = [];
+ my $applies = $obj->AppliedTo;
+ while ( my $queue = $applies->Next ) {
+ push @{ $rv->{Queue} }, $queue->Name;
+ }
}
}
my $attributes = $obj->Attributes;
while ( my $attribute = $attributes->Next ) {
my $content = $attribute->Content;
+ if ( $class eq 'Users' and $attribute->Name eq 'Bookmarks' ) {
+ next;
+ }
$rv->{Attributes}{ $attribute->Name } = $content
if length($content);
}
=head1 SYNOPSIS
- rt-dump-metdata [ 0 ]
+ rt-dump-metdata [--all]
=head1 DESCRIPTION
use the native database tools instead, as well as performing any necessary
steps from UPGRADING.
-When run without arguments, the metadata dump will only include 'local'
+This is NOT a tool for backing up an RT database. See also
+L<docs/initialdata> for more straightforward means of importing data.
+
+=head1 OPTIONS
+
+=over
+
+=item C<--all> or C<-a>
+
+When run with C<--all>, the dump will include all configuration
+metadata; otherwise, the metadata dump will only include 'local'
configuration changes, i.e. those done manually in the web interface.
-When run with the argument '0', the dump will include all configuration
-metadata.
+=item C<--limit-to-privileged> or C<-l>
+
+Causes the dumper to only dump privileged users.
+
+=item C<--skip-disabled> or C<-s>
+
+Ignores disabled rows in the database.
+
+=back
-This is NOT a tool for backing up an RT database.
+=cut
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
print "\t-p, --print\t"
. loc("Print the resulting digest messages to STDOUT; don't mail them. Do not mark them as sent")
. "\n";
+ print "\t-v, --verbose\t" . loc("Give output even on messages successfully sent") . "\n";
print "\t-h, --help\t" . loc("Print this message") . "\n";
if ( $error eq 'help' ) {
}
}
-my ( $frequency, $print, $help ) = ( '', '', '' );
+my ( $frequency, $print, $verbose, $help ) = ( '', '', '', '' );
GetOptions(
'mode=s' => \$frequency,
'print' => \$print,
+ 'verbose' => \$verbose,
'help' => \$help,
);
my ( $contents_list, $contents_body ) = build_digest_for_user( $user, $all_digest->{$user} );
# Now we have a content head and a content body. We can send a message.
if ( send_digest( $user, $contents_list, $contents_body ) ) {
- print "Sent message to $user\n";
+ print "Sent message to $user\n" if $verbose;
mark_transactions_sent( $frequency, $user, values %{$sent_transactions->{$user}} ) unless ($print);
} else {
print "Failed to send message to $user\n";
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
return;
}
-=head2 create NAME [--comment] [--group GNAME] [--user UNAME]
+=head2 create NAME [--comment] [--group GNAME] [--user NAME-OR-EMAIL]
Creates new action with NAME and adds users and/or groups to its
-recipient list. Would be notify as comment if --comment specified.
+recipient list. Would be notify as comment if --comment specified. The
+user, if specified, will be autocreated if necessary.
=cut
sub _check_groups
{
- return grep { $_ ? 1: do { print STDERR "Group '$_' skipped, doesn't exist\n"; 0; } }
- map { __check_group($_) } @_;
+ return map {$_->[1]}
+ grep { $_->[1] ? 1: do { print STDERR "Group '$_->[0]' skipped, doesn't exist\n"; 0; } }
+ map { [$_, __check_group($_)] } @_;
}
sub __check_group
sub _check_users
{
- return grep { $_ ? 1: do { print STDERR "User '$_' skipped, doesn't exist\n"; 0; } }
- map { __check_user($_) } @_;
+ return map {$_->[1]}
+ grep { $_->[1] ? 1: do { print STDERR "User '$_->[0]' skipped, doesn't exist and couldn't autocreate\n"; 0; } }
+ map { [$_, __check_user($_)] } @_;
}
sub __check_user
require RT::User;
my $obj = RT::User->new( RT->SystemUser );
$obj->Load( $instance );
+ $obj->LoadByEmail( $instance )
+ if not $obj->id and $instance =~ /@/;
+
+ unless ($obj->id) {
+ my ($ok, $msg) = $obj->Create(
+ Name => $instance,
+ EmailAddress => $instance,
+ Privileged => 0,
+ Comments => 'Autocreated when added to notify action via rt-email-group-admin',
+ );
+ print STDERR "Autocreate of user '$instance' failed: $msg\n"
+ unless $ok;
+ }
+
return $obj->id ? $obj : undef;
}
-=head2 add NAME [--group GNAME] [--user UNAME]
+=head2 add NAME [--group GNAME] [--user NAME-OR-EMAIL]
-Adds groups and/or users to recipients of the action NAME.
+Adds groups and/or users to recipients of the action NAME. The user, if
+specified, will be autocreated if necessary.
=cut
+++ /dev/null
-#!/usr/bin/perl
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
-# <sales@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-use strict;
-use warnings;
-no warnings 'once';
-
-# fix lib paths, some may be relative
-BEGIN {
- require File::Spec;
- my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
-}
-
-BEGIN {
- use RT;
- RT::LoadConfig();
- RT::Init();
-};
-use RT::Interface::CLI ();
-
-my %OPT = (
- help => 0,
- debug => 0,
-);
-my @OPT_LIST = qw(help|h! debug!);
-
-my $db_type = RT->Config->Get('DatabaseType');
-if ( $db_type eq 'Pg' ) {
- %OPT = (
- %OPT,
- limit => 0,
- all => 0,
- );
- push @OPT_LIST, 'limit=i', 'all!';
-}
-elsif ( $db_type eq 'mysql' ) {
- %OPT = (
- %OPT,
- limit => 0,
- all => 0,
- xmlpipe2 => 0,
- );
- push @OPT_LIST, 'limit=i', 'all!', 'xmlpipe2!';
-}
-elsif ( $db_type eq 'Oracle' ) {
- %OPT = (
- %OPT,
- memory => '2M',
- );
- push @OPT_LIST, qw(memory=s);
-}
-
-use Getopt::Long qw(GetOptions);
-GetOptions( \%OPT, @OPT_LIST );
-
-if ( $OPT{'help'} ) {
- RT::Interface::CLI->ShowHelp(
- Sections => 'NAME|DESCRIPTION|'. uc($db_type),
- );
-}
-
-my $fts_config = RT->Config->Get('FullTextSearch') || {};
-unless ( $fts_config->{'Enable'} ) {
- print STDERR <<EOT;
-
-Full text search is disabled in your RT configuration. Run
-/opt/rt3/sbin/rt-setup-fulltext-index to configure and enable it.
-
-EOT
- exit 1;
-}
-unless ( $fts_config->{'Indexed'} ) {
- print STDERR <<EOT;
-
-Full text search is enabled in your RT configuration, but not with any
-full-text database indexing -- hence this tool is not required. Read
-the documentation for %FullTextSearch in your RT_Config for more details.
-
-EOT
- exit 1;
-}
-
-if ( $db_type eq 'Oracle' ) {
- my $index = $fts_config->{'IndexName'} || 'rt_fts_index';
- $RT::Handle->dbh->do(
- "begin ctx_ddl.sync_index(?, ?); end;", undef,
- $index, $OPT{'memory'}
- );
- exit;
-} elsif ( $db_type eq 'mysql' ) {
- unless ($OPT{'xmlpipe2'}) {
- print STDERR <<EOT;
-
-Updates to the external Sphinx index are done via running the sphinx
-`indexer` tool:
-
- indexer rt
-
-EOT
- exit 1;
- }
-}
-
-my @types = qw(text html);
-foreach my $type ( @types ) {
- REDO:
- my $attachments = attachments($type);
- $attachments->Limit(
- FIELD => 'id',
- OPERATOR => '>',
- VALUE => last_indexed($type)
- );
- $attachments->OrderBy( FIELD => 'id', ORDER => 'asc' );
- $attachments->RowsPerPage( $OPT{'limit'} || 100 );
-
- my $found = 0;
- while ( my $a = $attachments->Next ) {
- next if filter( $type, $a );
- debug("Found attachment #". $a->id );
- my $txt = extract($type, $a) or next;
- $found++;
- process( $type, $a, $txt );
- debug("Processed attachment #". $a->id );
- }
- finalize( $type, $attachments ) if $found;
- clean( $type );
- goto REDO if $OPT{'all'} and $attachments->Count == ($OPT{'limit'} || 100)
-}
-
-sub attachments {
- my $type = shift;
- my $res = RT::Attachments->new( RT->SystemUser );
- my $txn_alias = $res->Join(
- ALIAS1 => 'main',
- FIELD1 => 'TransactionId',
- TABLE2 => 'Transactions',
- FIELD2 => 'id',
- );
- $res->Limit(
- ALIAS => $txn_alias,
- FIELD => 'ObjectType',
- VALUE => 'RT::Ticket',
- );
- my $ticket_alias = $res->Join(
- ALIAS1 => $txn_alias,
- FIELD1 => 'ObjectId',
- TABLE2 => 'Tickets',
- FIELD2 => 'id',
- );
- $res->Limit(
- ALIAS => $ticket_alias,
- FIELD => 'Status',
- OPERATOR => '!=',
- VALUE => 'deleted'
- );
-
- # On newer DBIx::SearchBuilder's, indicate that making the query DISTINCT
- # is unnecessary because the joins won't produce duplicates. This
- # drastically improves performance when fetching attachments.
- $res->{joins_are_distinct} = 1;
-
- return goto_specific(
- suffix => $type,
- error => "Don't know how to find $type attachments",
- arguments => [$res],
- );
-}
-
-sub last_indexed {
- my ($type) = (@_);
- return goto_specific(
- suffix => $db_type,
- error => "Don't know how to find last indexed $type attachment for $db_type DB",
- arguments => \@_,
- );
-}
-
-sub filter {
- my $type = shift;
- return goto_specific(
- suffix => $type,
- arguments => \@_,
- );
-}
-
-sub extract {
- my $type = shift;
- return goto_specific(
- suffix => $type,
- error => "No way to convert $type attachment into text",
- arguments => \@_,
- );
-}
-
-sub process {
- return goto_specific(
- suffix => $db_type,
- error => "No processer for $db_type DB",
- arguments => \@_,
- );
-}
-
-sub finalize {
- return goto_specific(
- suffix => $db_type,
- arguments => \@_,
- );
-}
-
-sub clean {
- return goto_specific(
- suffix => $db_type,
- arguments => \@_,
- );
-}
-
-{
-sub last_indexed_mysql {
- my $type = shift;
- my $attr = $RT::System->FirstAttribute('LastIndexedAttachments');
- return 0 unless $attr;
- return 0 unless exists $attr->{ $type };
- return $attr->{ $type } || 0;
-}
-
-sub process_mysql {
- my ($type, $attachment, $text) = (@_);
-
- my $doc = sphinx_template();
-
- my $element = $doc->createElement('sphinx:document');
- $element->setAttribute( id => $attachment->id );
- $element->appendTextChild( content => $$text );
-
- $doc->documentElement->appendChild( $element );
-}
-
-my $doc = undef;
-sub sphinx_template {
- return $doc if $doc;
-
- require XML::LibXML;
- $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
- my $root = $doc->createElement('sphinx:docset');
- $doc->setDocumentElement( $root );
-
- my $schema = $doc->createElement('sphinx:schema');
- $root->appendChild( $schema );
- foreach ( qw(content) ) {
- my $field = $doc->createElement('sphinx:field');
- $field->setAttribute( name => $_ );
- $schema->appendChild( $field );
- }
-
- return $doc;
-}
-
-sub finalize_mysql {
- my ($type, $attachments) = @_;
- sphinx_template()->toFH(*STDOUT, 1);
-}
-
-sub clean_mysql {
- $doc = undef;
-}
-
-}
-
-sub last_indexed_pg {
- my $type = shift;
- my $attachments = attachments( $type );
- my $alias = 'main';
- if ( $fts_config->{'Table'} && $fts_config->{'Table'} ne 'Attachments' ) {
- $alias = $attachments->Join(
- TYPE => 'left',
- FIELD1 => 'id',
- TABLE2 => $fts_config->{'Table'},
- FIELD2 => 'id',
- );
- }
- $attachments->Limit(
- ALIAS => $alias,
- FIELD => $fts_config->{'Column'},
- OPERATOR => 'IS NOT',
- VALUE => 'NULL',
- );
- $attachments->OrderBy( FIELD => 'id', ORDER => 'desc' );
- $attachments->RowsPerPage( 1 );
- my $res = $attachments->First;
- return 0 unless $res;
- return $res->id;
-}
-
-sub process_pg {
- my ($type, $attachment, $text) = (@_);
-
- my $dbh = $RT::Handle->dbh;
- my $table = $fts_config->{'Table'};
- my $column = $fts_config->{'Column'};
-
- my $query;
- if ( $table ) {
- if ( my ($id) = $dbh->selectrow_array("SELECT id FROM $table WHERE id = ?", undef, $attachment->id) ) {
- $query = "UPDATE $table SET $column = to_tsvector(?) WHERE id = ?";
- } else {
- $query = "INSERT INTO $table($column, id) VALUES(to_tsvector(?), ?)";
- }
- } else {
- $query = "UPDATE Attachments SET $column = to_tsvector(?) WHERE id = ?";
- }
-
- my $status = eval { $dbh->do( $query, undef, $$text, $attachment->id ) };
- unless ( $status ) {
- if ( $dbh->err == 7 && $dbh->state eq '54000' ) {
- warn "Attachment @{[$attachment->id]} cannot be indexed, as it contains too many unique words";
- } elsif ( $dbh->err == 7 && $dbh->state eq '22021' ) {
- warn "Attachment @{[$attachment->id]} cannot be indexed, as it contains invalid UTF8 bytes";
- } else {
- die "error: ". $dbh->errstr;
- }
-
- # Insert an empty tsvector, so we count this row as "indexed"
- # for purposes of knowing where to pick up
- eval { $dbh->do( $query, undef, "", $attachment->id ) }
- or die "Failed to insert empty tsvector: " . $dbh->errstr;
- }
-}
-
-sub attachments_text {
- my $res = shift;
- $res->Limit( FIELD => 'ContentType', VALUE => 'text/plain' );
- return $res;
-}
-
-sub extract_text {
- my $attachment = shift;
- my $text = $attachment->Content;
- return undef unless defined $text && length($text);
- return \$text;
-}
-
-sub attachments_html {
- my $res = shift;
- $res->Limit( FIELD => 'ContentType', VALUE => 'text/html' );
- return $res;
-}
-
-sub filter_html {
- my $attachment = shift;
- if ( my $parent = $attachment->ParentObj ) {
-# skip html parts that are alternatives
- return 1 if $parent->id
- && $parent->ContentType eq 'mulitpart/alternative';
- }
- return 0;
-}
-
-sub extract_html {
- my $attachment = shift;
- my $text = $attachment->Content;
- return undef unless defined $text && length($text);
-# TODO: html -> text
- return \$text;
-}
-
-sub goto_specific {
- my %args = (@_);
-
- my $func = (caller(1))[3];
- $func =~ s/.*:://;
- my $call = $func ."_". lc $args{'suffix'};
- unless ( defined &$call ) {
- return undef unless $args{'error'};
- require Carp; Carp::croak( $args{'error'} );
- }
- @_ = @{ $args{'arguments'} };
- goto &$call;
-}
-
-
-# helper functions
-sub debug { print @_, "\n" if $OPT{debug}; 1 }
-sub error { $RT::Logger->error(_(@_)); 1 }
-sub warning { $RT::Logger->warn(_(@_)); 1 }
-
-=head1 NAME
-
-rt-fulltext-indexer - Indexer for full text search
-
-=head1 DESCRIPTION
-
-This is a helper script to keep full text indexes in sync with data.
-Read F<docs/full_text_indexing.pod> for complete details on how and when
-to run it.
-
-=head1 AUTHOR
-
-Ruslan Zakirov E<lt>ruz@bestpractical.comE<gt>,
-Alex Vandiver E<lt>alexmv@bestpractical.comE<gt>
-
-=cut
-
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
my $status = eval { $dbh->do( $query, undef, $$text, $attachment->id ) };
unless ( $status ) {
if ( $dbh->err == 7 && $dbh->state eq '54000' ) {
- warn "Attachment @{[$attachment->id]} cannot be indexed, as it contains too many unique words";
+ warn "Attachment @{[$attachment->id]} cannot be indexed. Most probably it contains too many unique words. Error: ". $dbh->errstr;
} elsif ( $dbh->err == 7 && $dbh->state eq '22021' ) {
- warn "Attachment @{[$attachment->id]} cannot be indexed, as it contains invalid UTF8 bytes";
+ warn "Attachment @{[$attachment->id]} cannot be indexed. Most probably it contains invalid UTF8 bytes. Error: ". $dbh->errstr;
} else {
die "error: ". $dbh->errstr;
}
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
RT->InstallMode(1);
} else {
- RT->Init();
+ RT->Init( Heavy => 1 );
my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'post');
unless ( $status ) {
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
RT->InstallMode(1);
} else {
- RT->Init();
+ RT->Init( Heavy => 1 );
my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'post');
unless ( $status ) {
+++ /dev/null
-#!/usr/bin/perl
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-use strict;
-use warnings;
-
-# fix lib paths, some may be relative
-BEGIN {
- require File::Spec;
- my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
-}
-
-use Getopt::Long;
-my %opt;
-GetOptions( \%opt, 'help|h', );
-
-my $session_id = shift;
-
-if ( $opt{help} || !$session_id ) {
- require Pod::Usage;
- Pod::Usage::pod2usage({ verbose => 2 });
- exit;
-}
-
-require RT;
-RT::LoadConfig();
-RT::Init();
-
-require RT::Interface::Web::Session;
-my %session;
-
-tie %session, 'RT::Interface::Web::Session', $session_id;
-unless ( $session{'_session_id'} eq $session_id ) {
- print STDERR "Couldn't load session $session_id\n";
- exit 1;
-}
-
-use Data::Dumper;
-print "Content of session $session_id: ". Dumper( \%session);
-
-__END__
-
-=head1 NAME
-
-rt-session-viewer - show the content of a user's session
-
-=head1 SYNOPSIS
-
- # show the content of a session
- rt-session-viewer 2c21c8a2909c14eff12975dd2cc7b9a3
-
-=head1 DESCRIPTION
-
-This script deserializes and print content of a session identified
-by <session id>. May be useful for developers and for troubleshooting
-problems.
-
-=cut
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
my %args = (
dba => '@DB_DBA@',
+ package => 'RT',
);
GetOptions(
\%args,
'action=s',
'force', 'debug',
- 'dba=s', 'dba-password=s', 'prompt-for-dba-password',
+ 'dba=s', 'dba-password=s', 'prompt-for-dba-password', 'package=s',
'datafile=s', 'datadir=s', 'skip-create', 'root-password-file=s',
+ 'upgrade-from=s', 'upgrade-to=s',
'help|h',
);
my $db_type = RT->Config->Get('DatabaseType') || '';
my $db_host = RT->Config->Get('DatabaseHost') || '';
+my $db_port = RT->Config->Get('DatabasePort') || '';
my $db_name = RT->Config->Get('DatabaseName') || '';
my $db_user = RT->Config->Get('DatabaseUser') || '';
my $db_pass = RT->Config->Get('DatabasePassword') || '';
}
}
+my $version_word_regex = join '|', RT::Handle->version_words;
+my $version_dir = qr/^\d+\.\d+\.\d+(?:$version_word_regex)?\d*$/;
+
print "Working with:\n"
- ."Type:\t$db_type\nHost:\t$db_host\nName:\t$db_name\n"
+ ."Type:\t$db_type\nHost:\t$db_host\nPort:\t$db_port\nName:\t$db_name\n"
."User:\t$db_user\nDBA:\t$dba_user" . ($args{'skip-create'} ? ' (No DBA)' : '') . "\n";
foreach my $action ( @actions ) {
unless ( $args{'force'} ) {
print <<END;
-About to drop $db_type database $db_name on $db_host.
+About to drop $db_type database $db_name on $db_host (port '$db_port').
WARNING: This will erase all data in $db_name.
END
return (0, "Couldn't read dir '$base_dir' with upgrade data")
unless -d $base_dir || -r _;
- my $version_word_regex = join '|', RT::Handle->version_words;
my $upgrading_from = undef;
do {
if ( defined $upgrading_from ) {
print "Doesn't match #.#.#: ";
} else {
- print "Enter RT version you're upgrading from: ";
+ print "Enter $args{package} version you're upgrading from: ";
}
- $upgrading_from = scalar <STDIN>;
+ $upgrading_from = $args{'upgrade-from'} || scalar <STDIN>;
chomp $upgrading_from;
$upgrading_from =~ s/\s+//g;
- } while $upgrading_from !~ /^\d+\.\d+\.\d+(?:$version_word_regex)?\d*$/;
+ } while $upgrading_from !~ /$version_dir/;
my $upgrading_to = $RT::VERSION;
return (0, "The current version $upgrading_to is lower than $upgrading_from")
if ( defined $custom_upgrading_to ) {
print "Doesn't match #.#.#: ";
} else {
- print "\nEnter RT version if you want to stop upgrade at some point,\n";
+ print "\nEnter $args{package} version if you want to stop upgrade at some point,\n";
print " or leave it blank if you want apply above upgrades: ";
}
- $custom_upgrading_to = scalar <STDIN>;
+ $custom_upgrading_to = $args{'upgrade-to'} || scalar <STDIN>;
chomp $custom_upgrading_to;
$custom_upgrading_to =~ s/\s+//g;
last unless $custom_upgrading_to;
- } while $custom_upgrading_to !~ /^\d+\.\d+\.\d+(?:$version_word_regex)?\d*$/;
+ } while $custom_upgrading_to !~ /$version_dir/;
if ( $custom_upgrading_to ) {
return (
my ($base_dir, $from, $to) = @_;
opendir( my $dh, $base_dir ) or die "couldn't open dir: $!";
- my @versions = grep -d "$base_dir/$_" && /\d+\.\d+\.\d+/, readdir $dh;
+ my @versions = grep -d "$base_dir/$_" && /$version_dir/, readdir $dh;
closedir $dh;
+ die "\nERROR: No upgrade data found in '$base_dir'! Perhaps you specified the wrong --datadir?\n"
+ unless @versions;
+
return
grep defined $to ? RT::Handle::cmp_version($_, $to) <= 0 : 1,
grep RT::Handle::cmp_version($_, $from) > 0,
sub get_dba_password {
print "In order to create or update your RT database,"
. " this script needs to connect to your "
- . " $db_type instance on $db_host as $dba_user\n";
+ . " $db_type instance on $db_host (port '$db_port') as $dba_user\n";
print "Please specify that user's database password below. If the user has no database\n";
print "password, just press return.\n\n";
print "Password: ";
for 'init' and 'insert': rather than using the default administrative password
for RT's "root" user, use the password in this file.
+=item upgrade-from
+
+for 'upgrade': specifies the version to upgrade from, and do not prompt
+for it if it appears to be a valid version.
+
+=item upgrade-to
+
+for 'upgrade': specifies the version to upgrade to, and do not prompt
+for it if it appears to be a valid version.
+
=back
+++ /dev/null
-#!/usr/bin/perl
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
-# <sales@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-use strict;
-use warnings;
-no warnings 'once';
-
-# fix lib paths, some may be relative
-BEGIN {
- require File::Spec;
- my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
-}
-
-BEGIN {
- use RT;
- RT::LoadConfig();
- RT::Init();
-};
-use RT::Interface::CLI ();
-
-my %DB = (
- type => scalar RT->Config->Get('DatabaseType'),
- user => scalar RT->Config->Get('DatabaseUser'),
- admin => 'freeside',
- admin_password => undef,
-);
-
-my %OPT = (
- help => 0,
- ask => 1,
- dryrun => 0,
- attachments => 1,
-);
-
-my %DEFAULT;
-if ( $DB{'type'} eq 'Pg' ) {
- %DEFAULT = (
- table => 'Attachments',
- column => 'ContentIndex',
- );
-}
-elsif ( $DB{'type'} eq 'mysql' ) {
- %DEFAULT = (
- table => 'AttachmentsIndex',
- );
-}
-elsif ( $DB{'type'} eq 'Oracle' ) {
- %DEFAULT = (
- prefix => 'rt_fts_',
- );
-}
-
-use Getopt::Long qw(GetOptions);
-GetOptions(
- 'h|help!' => \$OPT{'help'},
- 'ask!' => \$OPT{'ask'},
- 'dry-run!' => \$OPT{'dryrun'},
- 'attachments!' => \$OPT{'attachments'},
-
- 'table=s' => \$OPT{'table'},
- 'column=s' => \$OPT{'column'},
- 'url=s' => \$OPT{'url'},
- 'maxmatches=i' => \$OPT{'maxmatches'},
- 'index-type=s' => \$OPT{'index-type'},
-
- 'dba=s' => \$DB{'admin'},
- 'dba-password=s' => \$DB{'admin_password'},
-) or show_help();
-
-if ( $OPT{'help'} || (!$DB{'admin'} && $DB{'type'} eq 'Oracle' ) ) {
- show_help( !$OPT{'help'} );
-}
-
-my $dbh = $RT::Handle->dbh;
-$dbh->{'RaiseError'} = 1;
-$dbh->{'PrintError'} = 1;
-
-if ( $DB{'type'} eq 'mysql' ) {
- check_sphinx();
- my $table = $OPT{'table'} || prompt(
- message => "Enter name of a new MySQL table that will be used to connect to the\n"
- . "Sphinx server:",
- default => $DEFAULT{'table'},
- silent => !$OPT{'ask'},
- );
- my $url = $OPT{'url'} || prompt(
- message => "Enter URL of the sphinx search server; this should be of the form\n"
- . "sphinx://<server>:<port>/<index name>",
- default => 'sphinx://localhost:3312/rt',
- silent => !$OPT{'ask'},
- );
- my $maxmatches = $OPT{'maxmatches'} || prompt(
- message => "Maximum number of matches to return; this is the maximum number of\n"
- . "attachment records returned by the search, not the maximum number\n"
- . "of tickets. Both your RT_SiteConfig.pm and your sphinx.conf must\n"
- . "agree on this value. Larger values cause your Sphinx server to\n"
- . "consume more memory and CPU time per query.",
- default => 10000,
- silent => !$OPT{'ask'},
- );
-
- my $schema = <<END;
-CREATE TABLE $table (
- id INTEGER UNSIGNED NOT NULL,
- weight INTEGER NOT NULL,
- query VARCHAR(3072) NOT NULL,
- INDEX(query)
-) ENGINE=SPHINX CONNECTION="$url" CHARACTER SET utf8
-END
-
- do_error_is_ok( dba_handle() => "DROP TABLE $table" )
- unless $OPT{'dryrun'};
- insert_schema( $schema );
-
- print_rt_config( Table => $table, MaxMatches => $maxmatches );
-
- require URI;
- my $urlo = URI->new( $url );
- my ($host, $port) = split /:/, $urlo->authority;
- my $index = $urlo->path;
- $index =~ s{^/+}{};
-
- my $var_path = $RT::VarPath;
-
- my %sphinx_conf = ();
- $sphinx_conf{'host'} = RT->Config->Get('DatabaseHost');
- $sphinx_conf{'db'} = RT->Config->Get('DatabaseName');
- $sphinx_conf{'user'} = RT->Config->Get('DatabaseUser');
- $sphinx_conf{'pass'} = RT->Config->Get('DatabasePassword');
-
- print <<END
-
-Below is a simple Sphinx configuration which can be used to index all
-text/plain attachments in your database. This configuration is not
-ideal; you should read the Sphinx documentation to understand how to
-configure it to better suit your needs.
-
-source rt {
- type = mysql
-
- sql_host = $sphinx_conf{'host'}
- sql_db = $sphinx_conf{'db'}
- sql_user = $sphinx_conf{'user'}
- sql_pass = $sphinx_conf{'pass'}
-
- sql_query_pre = SET NAMES utf8
- sql_query = \\
- SELECT a.id, a.content FROM Attachments a \\
- JOIN Transactions txn ON a.TransactionId = txn.id AND txn.ObjectType = 'RT::Ticket' \\
- JOIN Tickets t ON txn.ObjectId = t.id \\
- WHERE a.ContentType = 'text/plain' AND t.Status != 'deleted'
-
- sql_query_info = SELECT * FROM Attachments WHERE id=\$id
-}
-
-index $index {
- source = rt
- path = $var_path/sphinx/index
- docinfo = extern
- charset_type = utf-8
-}
-
-indexer {
- mem_limit = 32M
-}
-
-searchd {
- port = $port
- log = $var_path/sphinx/searchd.log
- query_log = $var_path/sphinx/query.log
- read_timeout = 5
- max_children = 30
- pid_file = $var_path/sphinx/searchd.pid
- max_matches = $maxmatches
- seamless_rotate = 1
- preopen_indexes = 0
- unlink_old = 1
-}
-
-END
-
-}
-elsif ( $DB{'type'} eq 'Pg' ) {
- check_tsvalue();
- my $table = $OPT{'table'} || prompt(
- message => "Enter the name of a DB table that will be used to store the Pg tsvector.\n"
- . "You may either use the existing Attachments table, or create a new\n"
- . "table.",
- default => $DEFAULT{'table'},
- silent => !$OPT{'ask'},
- );
- my $column = $OPT{'column'} || prompt(
- message => 'Enter the name of a column that will be used to store the Pg tsvector:',
- default => $DEFAULT{'column'},
- silent => !$OPT{'ask'},
- );
-
- my $schema;
- my $drop;
- if ( lc($table) eq 'attachments' ) {
- $drop = "ALTER TABLE $table DROP COLUMN $column";
- $schema = "ALTER TABLE $table ADD COLUMN $column tsvector";
- } else {
- $drop = "DROP TABLE $table";
- $schema = "CREATE TABLE $table ( "
- ."id INTEGER NOT NULL,"
- ."$column tsvector )";
- }
-
- my $index_type = lc($OPT{'index-type'} || '');
- while ( $index_type ne 'gist' and $index_type ne 'gin' ) {
- $index_type = lc prompt(
- message => "You may choose between GiST or GIN indexes; the former is several times\n"
- . "slower to search, but takes less space on disk and is faster to update.",
- default => 'GiST',
- silent => !$OPT{'ask'},
- );
- }
-
- do_error_is_ok( dba_handle() => $drop )
- unless $OPT{'dryrun'};
- insert_schema( $schema );
- insert_schema("CREATE INDEX ${column}_idx ON $table USING $index_type($column)");
-
- print_rt_config( Table => $table, Column => $column );
-}
-elsif ( $DB{'type'} eq 'Oracle' ) {
- {
- my $dbah = dba_handle();
- do_print_error( $dbah => 'GRANT CTXAPP TO '. $DB{'user'} );
- do_print_error( $dbah => 'GRANT EXECUTE ON CTXSYS.CTX_DDL TO '. $DB{'user'} );
- }
-
- my %PREFERENCES = (
- datastore => {
- type => 'DIRECT_DATASTORE',
- },
- filter => {
- type => 'AUTO_FILTER',
-# attributes => {
-# timeout => 120, # seconds
-# timeout_type => 'HEURISTIC', # or 'FIXED'
-# },
- },
- lexer => {
- type => 'WORLD_LEXER',
- },
- word_list => {
- type => 'BASIC_WORDLIST',
- attributes => {
- stemmer => 'AUTO',
- fuzzy_match => 'AUTO',
-# fuzzy_score => undef,
-# fuzzy_numresults => undef,
-# substring_index => undef,
-# prefix_index => undef,
-# prefix_length_min => undef,
-# prefix_length_max => undef,
-# wlidcard_maxterms => undef,
- },
- },
- 'section_group' => {
- type => 'NULL_SECTION_GROUP',
- },
-
- storage => {
- type => 'BASIC_STORAGE',
- attributes => {
- R_TABLE_CLAUSE => 'lob (data) store as (cache)',
- I_INDEX_CLAUSE => 'compress 2',
- },
- },
- );
-
- my @params = ();
- push @params, ora_create_datastore( %{ $PREFERENCES{'datastore'} } );
- push @params, ora_create_filter( %{ $PREFERENCES{'filter'} } );
- push @params, ora_create_lexer( %{ $PREFERENCES{'lexer'} } );
- push @params, ora_create_word_list( %{ $PREFERENCES{'word_list'} } );
- push @params, ora_create_stop_list();
- push @params, ora_create_section_group( %{ $PREFERENCES{'section_group'} } );
- push @params, ora_create_storage( %{ $PREFERENCES{'storage'} } );
-
- my $index_params = join "\n", @params;
- my $index_name = $DEFAULT{prefix} .'index';
- do_error_is_ok( $dbh => "DROP INDEX $index_name" )
- unless $OPT{'dryrun'};
- $dbh->do(
- "CREATE INDEX $index_name ON Attachments(Content)
- indextype is ctxsys.context parameters('
- $index_params
- ')",
- ) unless $OPT{'dryrun'};
-
- print_rt_config( IndexName => $index_name );
-}
-else {
- die "Full-text indexes on $DB{type} are not yet supported";
-}
-
-sub check_tsvalue {
- my $dbh = $RT::Handle->dbh;
- my $fts = ($dbh->selectrow_array(<<EOQ))[0];
-SELECT 1 FROM information_schema.routines WHERE routine_name = 'plainto_tsquery'
-EOQ
- unless ($fts) {
- print STDERR <<EOT;
-
-Your PostgreSQL server does not include full-text support. You will
-need to upgrade to PostgreSQL version 8.3 or higher to use full-text
-indexing.
-
-EOT
- exit 1;
- }
-}
-
-sub check_sphinx {
- return if $RT::Handle->CheckSphinxSE;
-
- print STDERR <<EOT;
-
-Your MySQL server has not been compiled with the Sphinx storage engine
-(sphinxse). You will need to recompile MySQL according to the
-instructions in Sphinx's documentation at
-http://sphinxsearch.com/docs/current.html#sphinxse-installing
-
-EOT
- exit 1;
-}
-
-sub ora_create_datastore {
- return sprintf 'datastore %s', ora_create_preference(
- @_,
- name => 'datastore',
- );
-}
-
-sub ora_create_filter {
- my $res = '';
- $res .= sprintf "format column %s\n", ora_create_format_column();
- $res .= sprintf 'filter %s', ora_create_preference(
- @_,
- name => 'filter',
- );
- return $res;
-}
-
-sub ora_create_lexer {
- return sprintf 'lexer %s', ora_create_preference(
- @_,
- name => 'lexer',
- );
-}
-
-sub ora_create_word_list {
- return sprintf 'wordlist %s', ora_create_preference(
- @_,
- name => 'word_list',
- );
-}
-
-sub ora_create_stop_list {
- my $file = shift || 'etc/stopwords/en.txt';
- return '' unless -e $file;
-
- my $name = $DEFAULT{'prefix'} .'stop_list';
- unless ($OPT{'dryrun'}) {
- do_error_is_ok( $dbh => 'begin ctx_ddl.drop_stoplist(?); end;', $name );
-
- $dbh->do(
- 'begin ctx_ddl.create_stoplist(?, ?); end;',
- undef, $name, 'BASIC_STOPLIST'
- );
-
- open( my $fh, '<:utf8', $file )
- or die "couldn't open file '$file': $!";
- while ( my $word = <$fh> ) {
- chomp $word;
- $dbh->do(
- 'begin ctx_ddl.add_stopword(?, ?); end;',
- undef, $name, $word
- );
- }
- close $fh;
- }
- return sprintf 'stoplist %s', $name;
-}
-
-sub ora_create_section_group {
- my %args = @_;
- my $name = $DEFAULT{'prefix'} .'section_group';
- unless ($OPT{'dryrun'}) {
- do_error_is_ok( $dbh => 'begin ctx_ddl.drop_section_group(?); end;', $name );
- $dbh->do(
- 'begin ctx_ddl.create_section_group(?, ?); end;',
- undef, $name, $args{'type'}
- );
- }
- return sprintf 'section group %s', $name;
-}
-
-sub ora_create_storage {
- return sprintf 'storage %s', ora_create_preference(
- @_,
- name => 'storage',
- );
-}
-
-sub ora_create_format_column {
- my $column_name = 'ContentOracleFormat';
- return $column_name if $OPT{'dryrun'};
- unless (
- $dbh->column_info(
- undef, undef, uc('Attachments'), uc( $column_name )
- )->fetchrow_array
- ) {
- $dbh->do(qq{
- ALTER TABLE Attachments ADD $column_name VARCHAR2(10)
- });
- }
-
- my $detect_format = qq{
- CREATE OR REPLACE FUNCTION $DEFAULT{prefix}detect_format_simple(
- parent IN NUMBER,
- type IN VARCHAR2,
- encoding IN VARCHAR2,
- fname IN VARCHAR2
- )
- RETURN VARCHAR2
- AS
- format VARCHAR2(10);
- BEGIN
- format := CASE
- };
- unless ( $OPT{'attachments'} ) {
- $detect_format .= qq{
- WHEN fname IS NOT NULL THEN 'ignore'
- };
- }
- $detect_format .= qq{
- WHEN type = 'text' THEN 'text'
- WHEN type = 'text/rtf' THEN 'ignore'
- WHEN type LIKE 'text/%' THEN 'text'
- WHEN type LIKE 'message/%' THEN 'text'
- ELSE 'ignore'
- END;
- RETURN format;
- END;
- };
- ora_create_procedure( $detect_format );
-
- $dbh->do(qq{
- UPDATE Attachments
- SET $column_name = $DEFAULT{prefix}detect_format_simple(
- Parent,
- ContentType, ContentEncoding,
- Filename
- )
- WHERE $column_name IS NULL
- });
- $dbh->do(qq{
- CREATE OR REPLACE TRIGGER $DEFAULT{prefix}set_format
- BEFORE INSERT
- ON Attachments
- FOR EACH ROW
- BEGIN
- :new.$column_name := $DEFAULT{prefix}detect_format_simple(
- :new.Parent,
- :new.ContentType, :new.ContentEncoding,
- :new.Filename
- );
- END;
- });
- return $column_name;
-}
-
-sub ora_create_preference {
- my %info = @_;
- my $name = $DEFAULT{'prefix'} . $info{'name'};
- return $name if $OPT{'dryrun'};
- do_error_is_ok( $dbh => 'begin ctx_ddl.drop_preference(?); end;', $name );
- $dbh->do(
- 'begin ctx_ddl.create_preference(?, ?); end;',
- undef, $name, $info{'type'}
- );
- return $name unless $info{'attributes'};
-
- while ( my ($attr, $value) = each %{ $info{'attributes'} } ) {
- $dbh->do(
- 'begin ctx_ddl.set_attribute(?, ?, ?); end;',
- undef, $name, $attr, $value
- );
- }
-
- return $name;
-}
-
-sub ora_create_procedure {
- my $text = shift;
-
- return if $OPT{'dryrun'};
- my $status = $dbh->do($text, { RaiseError => 0 });
-
- # Statement succeeded
- return if $status;
-
- if ( 6550 != $dbh->err ) {
- # Utter failure
- die $dbh->errstr;
- }
- else {
- my $msg = $dbh->func( 'plsql_errstr' );
- die $dbh->errstr if !defined $msg;
- die $msg if $msg;
- }
-}
-
-sub dba_handle {
- if ( $DB{'type'} eq 'Oracle' ) {
- $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8";
- $ENV{'NLS_NCHAR'} = "AL32UTF8";
- }
- my $dsn = do { my $h = new RT::Handle; $h->BuildDSN; $h->DSN };
- my $dbh = DBI->connect(
- $dsn, $DB{admin}, $DB{admin_password},
- { RaiseError => 1, PrintError => 1 },
- );
- unless ( $dbh ) {
- die "Failed to connect to $dsn as user '$DB{admin}': ". $DBI::errstr;
- }
- return $dbh;
-}
-
-sub do_error_is_ok {
- my $dbh = shift;
- local $dbh->{'RaiseError'} = 0;
- local $dbh->{'PrintError'} = 0;
- return $dbh->do(shift, undef, @_);
-}
-
-sub do_print_error {
- my $dbh = shift;
- local $dbh->{'RaiseError'} = 0;
- local $dbh->{'PrintError'} = 1;
- return $dbh->do(shift, undef, @_);
-}
-
-sub prompt {
- my %args = ( @_ );
- return $args{'default'} if $args{'silent'};
-
- local $| = 1;
- print $args{'message'};
- if ( $args{'default'} ) {
- print "\n[". $args{'default'} .']: ';
- } else {
- print ":\n";
- }
-
- my $res = <STDIN>;
- chomp $res;
- print "\n";
- return $args{'default'} if !$res && $args{'default'};
- return $res;
-}
-
-sub verbose { print @_, "\n" if $OPT{verbose} || $OPT{verbose}; 1 }
-sub debug { print @_, "\n" if $OPT{debug}; 1 }
-sub error { $RT::Logger->error( @_ ); verbose(@_); 1 }
-sub warning { $RT::Logger->warning( @_ ); verbose(@_); 1 }
-
-sub show_help {
- my $error = shift;
- RT::Interface::CLI->ShowHelp(
- ExitValue => $error,
- Sections => 'NAME|DESCRIPTION',
- );
-}
-
-sub print_rt_config {
- my %args = @_;
- my $config = <<END;
-
-You can now configure RT to use the newly-created full-text index by
-adding the following to your RT_SiteConfig.pm:
-
-Set( %FullTextSearch,
- Enable => 1,
- Indexed => 1,
-END
-
- $config .= sprintf(" %-10s => '$args{$_}',\n",$_)
- foreach grep defined $args{$_}, keys %args;
- $config .= ");\n";
-
- print $config;
-}
-
-sub insert_schema {
- my $dbh = dba_handle();
- my $message = "Going to run the following in the DB:";
- my $schema = shift;
- print "$message\n";
- my $disp = $schema;
- $disp =~ s/^/ /mg;
- print "$disp\n\n";
- return if $OPT{'dryrun'};
-
- my $res = $dbh->do( $schema );
- unless ( $res ) {
- die "Couldn't run DDL query: ". $dbh->errstr;
- }
-}
-
-=head1 NAME
-
-rt-setup-fulltext-index - Create indexes for full text search
-
-=head1 DESCRIPTION
-
-This script creates the appropriate tables, columns, functions, and / or
-views necessary for full-text searching for your database type. It will
-drop any existing indexes in the process.
-
-Please read F<docs/full_text_indexing.pod> for complete documentation on
-full-text indexing for your database type.
-
-If you have a non-standard database administrator user or password, you
-may use the C<--dba> and C<--dba-password> parameters to set them
-explicitly:
-
- rt-setup-fulltext-index --dba sysdba --dba-password 'secret'
-
-To test what will happen without running any DDL, pass the C<--dryrun>
-flag.
-
-The Oracle index determines which content-types it will index at
-creation time. By default, textual message bodies and textual uploaded
-attachments (attachments with filenames) are indexed; to ignore textual
-attachments, pass the C<--no-attachments> flag when the index is
-created.
-
-
-=head1 AUTHOR
-
-Ruslan Zakirov E<lt>ruz@bestpractical.comE<gt>,
-Alex Vandiver E<lt>alexmv@bestpractical.comE<gt>
-
-=cut
-
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
}
-use RT::Shredder ();
+use RT -init;
+
+require RT::Shredder;
+
use Getopt::Long qw(GetOptions);
use File::Spec ();
our %opt;
parse_args();
-RT::Shredder::Init( %opt );
my $shredder = RT::Shredder->new;
{
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
Plack::Middleware::Test::StashWarnings 0.06
Test::LongString
Test::NoWarnings
+Locale::PO
.
$deps{'FASTCGI'} = [ text_to_hash( << '.') ];
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
+++ /dev/null
-#!/usr/bin/perl -w
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
-# <sales@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-use warnings;
-use strict;
-
-# fix lib paths, some may be relative
-BEGIN {
- die <<EOT if ${^TAINT};
-RT does not run under Perl's "taint mode". Remove -T from the command
-line, or remove the PerlTaintCheck parameter from your mod_perl
-configuration.
-EOT
-
- require File::Spec;
- my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
-
-}
-
-use Getopt::Long;
-no warnings 'once';
-
-if (grep { m/help/ } @ARGV) {
- require Pod::Usage;
- print Pod::Usage::pod2usage( { verbose => 2 } );
- exit;
-}
-
-require RT;
-RT->LoadConfig();
-RT->InitPluginPaths();
-RT->InitLogging();
-require Module::Refresh if RT->Config->Get('DevelMode');
-
-require RT::Handle;
-my ($integrity, $state, $msg) = RT::Handle->CheckIntegrity;
-
-unless ( $integrity ) {
- print STDERR <<EOF;
-
-RT couldn't connect to the database where tickets are stored.
-If this is a new installation of RT, you should visit the URL below
-to configure RT and initialize your database.
-
-If this is an existing RT installation, this may indicate a database
-connectivity problem.
-
-The error RT got back when trying to connect to your database was:
-
-$msg
-
-EOF
-
- require RT::Installer;
- # don't enter install mode if the file exists but is unwritable
- if (-e RT::Installer->ConfigFile && !-w _) {
- die 'Since your configuration exists ('
- . RT::Installer->ConfigFile
- . ") but is not writable, I'm refusing to do anything.\n";
- }
-
- RT->Config->Set( 'LexiconLanguages' => '*' );
- RT::I18N->Init;
-
- RT->InstallMode(1);
-} else {
- RT->Init();
-
- my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'post');
- unless ( $status ) {
- print STDERR $msg, "\n\n";
- exit -1;
- }
-}
-
-# we must disconnect DB before fork
-if ($RT::Handle) {
- $RT::Handle->dbh(undef);
- undef $RT::Handle;
-}
-
-require RT::Interface::Web::Handler;
-my $app = RT::Interface::Web::Handler->PSGIApp;
-
-if ($ENV{RT_TESTING}) {
- my $screen_logger = $RT::Logger->remove('screen');
- require Log::Dispatch::Perl;
- $RT::Logger->add(
- Log::Dispatch::Perl->new(
- name => 'rttest',
- min_level => $screen_logger->min_level,
- action => {
- error => 'warn',
- critical => 'warn'
- }
- )
- );
- require Plack::Middleware::Test::StashWarnings;
- $app = Plack::Middleware::Test::StashWarnings->wrap($app);
-}
-
-# when used as a psgi file
-if (caller) {
- return $app;
-}
-
-
-# load appropriate server
-
-require Plack::Runner;
-
-my $is_fastcgi = $0 =~ m/fcgi$/;
-my $r = Plack::Runner->new( $0 =~ /standalone/ ? ( server => 'Standalone' ) :
- $is_fastcgi ? ( server => 'FCGI' )
- : (),
- env => 'deployment' );
-
-# figure out the port
-my $port;
-
-# handle "rt-server 8888" for back-compat, but complain about it
-if ($ARGV[0] && $ARGV[0] =~ m/^\d+$/) {
- warn "Deprecated: please run $0 --port $ARGV[0] instead\n";
- unshift @ARGV, '--port';
-}
-
-my @args = @ARGV;
-
-use List::MoreUtils 'last_index';
-my $last_index = last_index { $_ eq '--port' } @args;
-
-my $explicit_port;
-
-if ( $last_index != -1 && $args[$last_index+1] =~ /^\d+$/ ) {
- $explicit_port = $args[$last_index+1];
- $port = $explicit_port;
-
- # inform the rest of the system what port we manually chose
- my $old_app = $app;
- $app = sub {
- my $env = shift;
-
- $env->{'rt.explicit_port'} = $port;
-
- $old_app->($env, @_);
- };
-}
-else {
- # default to the configured WebPort and inform Plack::Runner
- $port = RT->Config->Get('WebPort') || '8080';
- push @args, '--port', $port;
-}
-
-push @args, '--server', 'Standalone' if RT->InstallMode;
-push @args, '--server', 'Starlet' unless $r->{server} || grep { m/--server/ } @args;
-
-$r->parse_options(@args);
-
-delete $r->{options} if $is_fastcgi; ### mangle_host_port_socket ruins everything
-
-unless ($r->{env} eq 'development') {
- push @{$r->{options}}, server_ready => sub {
- my($args) = @_;
- my $name = $args->{server_software} || ref($args); # $args is $server
- my $host = $args->{host} || 0;
- my $proto = $args->{proto} || 'http';
- print STDERR "$name: Accepting connections at $proto://$host:$args->{port}/\n";
- };
-}
-eval { $r->run($app) };
-if (my $err = $@) {
- handle_startup_error($err);
-}
-
-exit 0;
-
-sub handle_startup_error {
- my $err = shift;
- if ( $err =~ /listen/ ) {
- handle_bind_error();
- } else {
- die
- "Something went wrong while trying to run RT's standalone web server:\n\t"
- . $err;
- }
-}
-
-
-sub handle_bind_error {
-
- print STDERR <<EOF;
-WARNING: RT couldn't start up a web server on port @{[$port]}.
-This is often the case if the port is already in use or you're running @{[$0]}
-as someone other than your system's "root" user. You may also specify a
-temporary port with: $0 --port <port>
-EOF
-
- if ($explicit_port) {
- print STDERR
- "Please check your system configuration or choose another port\n\n";
- }
-}
-
-__END__
-
-=head1 NAME
-
-rt-server - RT standalone server
-
-=head1 SYNOPSIS
-
- # runs prefork server listening on port 8080, requires Starlet
- rt-server --port 8080
-
- # runs server listening on port 8080
- rt-server --server Standalone --port 8080
- # or
- standalone_httpd --port 8080
-
- # runs other PSGI server on port 8080
- rt-server --server Starman --port 8080
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
RT->InstallMode(1);
} else {
- RT->Init();
+ RT->Init( Heavy => 1 );
my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'post');
unless ( $status ) {
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<%ARGS>
$id => undef
$TypeComposite => undef
-$LookupType => undef
+$LookupType => RT::Ticket->CustomFieldLookupType
$MaxValues => undef
$SortOrder => undef
$Description => undef
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
my %right_categories = %{$Context->RightCategories};
for my $right (keys %available_rights) {
- push @{$categories{$right_categories{$right}}}, $right;
+ my $category = $right_categories{$right} || 'Miscellaneous'; # loc
+ push @{$categories{$category}}, $right;
}
}
'Status' => loc('Status changes'),
);
-my %catsort = ( General => 1, Staff => 2, Admin => 3, Status => 4 );
+my %catsort = ( General => 1, Staff => 2, Admin => 3, Status => 4, Miscellaneous => 999 );
+my $i = 5;
+for my $category ( sort keys %categories ) {
+ next if $catsort{$category};
+ $catsort{$category} = $i++;
+}
$acldesc ||= join '-', ($Principal ? $Principal->PrincipalId : 'addprincipal'),
ref($Context), $Context->Id;
<div class="category-tabs">
<ul>
% for my $category (sort { $catsort{$a} <=> $catsort{$b} } keys %categories) {
- <li><a href="#<% "$id-$category" %>"><% $category_desc{$category} || loc('Miscellaneous') %></a></li>
+ <li><a href="#<% "$id-$category" %>"><% $category_desc{$category} || loc($category) %></a></li>
% }
</ul>
% for my $category (sort { $catsort{$a} <=> $catsort{$b} } keys %categories) {
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
my @types = $cf->RenderTypes($TypeComposite);
-# XXX: We currently don't support cascaded select CFs when
-# rendering as a list, so don't offer it for now.
-if ( $BasedOn ) {
- @types = grep { not /List/ } @types;
-}
</%INIT>
<%ARGS>
$Default => undef
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
jQuery(event.target).val(ui.item.value);
jQuery(event.target).closest("form").submit();
}
- });
+ }).addClass("autocompletes-user");
});
</script>
% }
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<& /Elements/CollectionList,
OrderBy => 'Name',
Order => 'ASC',
+ Rows => 50,
%ARGS,
Format => $Format,
Collection => $queues,
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
.text(v[0]));
});
- $("style#sitecss").text($('#user_css').val());
+ function update_sitecss(text) {
+ if (!text)
+ text = $('#user_css').val();
+
+ // IE 8 doesn't let us update the innerHTML of <style> tags (with jQuery.text())
+ // see: http://stackoverflow.com/questions/2692770/style-style-textcss-appendtohead-does-not-work-in-ie/2692861#2692861
+ $("style#sitecss").remove();
+ $("<style id='sitecss' type='text/css' media='all'>" + text + "</style>").appendTo('head');
+ }
+
+ update_sitecss();
$('#try').click(function() {
- $("style#sitecss").text($('#user_css').val());
+ update_sitecss();
});
$('#reset').click(function() {
setTimeout(function() {
- $("style#sitecss").text($('#user_css').val());
+ update_sitecss();
}, 1000);
});
}
}
$('#user_css').val(css);
- $("style#sitecss").text(css);
+ update_sitecss(css);
}
$('#color-picker').farbtastic(function(color){ change_color(color, this.hsl[2] > <% $text_threshold %> ? '#000' : '#fff') });
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
form.find('input[name=UserOp]').val('=');
form.submit();
}
- });
+ }).addClass("autocompletes-user");
});
</script>
</form>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
my $ticket = LoadTicket($1);
+ my $skip_update = 0;
+ $m->callback( CallbackName => 'BeforeApproval',
+ skip_update => \$skip_update,
+ Ticket => $ticket,
+ actions => \@actions);
+ next if $skip_update;
+
if ( $ARGS{ "Approval-" . $ticket->Id . "-Notes" } ) {
my $notes = MIME::Entity->build(
Data => [ $ARGS{ "Approval-" . $ticket->Id . "-Notes" } ]
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% $Classes->LimitToEnabled();
% while (my $Class = $Classes->Next) {
<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%>
+<%$Class->Description || ''%>
<div id="topics-<%$Class->Id%>" style="display: none">
<form action="ExtractFromTicket.html">
<input type="hidden" name="Ticket" value="<% $Ticket %>" />
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# END BPS TAGGED BLOCK }}}
<& /Elements/Header, Title => loc('Create an article in class...') &>
-<& /Elements/Tabs, Title => loc('Create an article in class...') &>
+<& /Elements/Tabs &>
<ul>
% my $Classes = RT::Classes->new($session{'CurrentUser'});
% $Classes->LimitToEnabled();
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% my $edit = RT->Config->Get('WebPath') . '/Dashboards/Modify.html?id='.$id;
% my $subscription = RT->Config->Get('WebPath') . '/Dashboards/Subscription.html?id='.$id;
<p><&|/l, $edit, $subscription &>You <a href="[_1]">may edit this dashboard</a> and <a href="[_2]">your subscription</a> to it in RT.</&></p>
-% }
-%# Matches the closing div in /Elements/Footer that it expects
-%# from PageLayout (which we don't use here)
-<div>
+%# We disable autohandlers when mailing (!$Preview) so /Elements/Footer isn't
+%# run for us. Tidy up the end of the HTML. We don't use /Elements/Tabs (and
+%# hence PageLayout) so we don't need to close any other tags.
+ </body>
+</html>
+% }
<%INIT>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
if ($col =~ /^$RE{quoted}$/o) {
substr($col,0,1) = "";
substr($col,-1,1) = "";
+ $col =~ s/\\(.)/$1/g;
}
my $colref = { };
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
# collection is ordered or not
if ( @OrderBy && ($AllowSorting || !$Collection->{'order_by'}) ) {
if ( $OrderBy[0] =~ /\|/ ) {
- @OrderBy = grep length($_), split /\|/, $OrderBy[0];
+ @OrderBy = split /\|/, $OrderBy[0];
@Order = split /\|/,$Order[0];
}
+ @OrderBy = grep length, @OrderBy;
$Collection->OrderByCols(
map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } }
( 0 .. $#OrderBy )
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
# Display custom field contents, separated by newlines.
# For Image custom fields we also show a thumbnail here.
-
my $values = $cf->ValuesForObject( $_[0] );
return if $values->Count == 0;
+
my @values;
# it is guaranteed to be the same type for all fields, right?
my $v = $values->First;
my $arg = $DECODED_ARGS->{ $name };
my $checked = '';
if ( $arg && ref $arg ) {
- $checked = 'checked="checked"' if grep $_ == $id, @$arg;
+ $checked = 'checked="checked"' if grep $_ == $id, grep { defined and length } @$arg;
}
elsif ( $arg ) {
$checked = 'checked="checked"' if $arg == $id;
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%# END BPS TAGGED BLOCK }}}
% while ( $Values and my $value = $Values->Next ) {
%# XXX - let user download the file(s) here?
-<input type="checkbox" class="checkbox" name="<%$NamePrefix%><%$CustomField->Id%>-DeleteValueIds" class="CF-<%$CustomField->id%>-Edit" value="<% $value->Id %>" /><a href="<%RT->Config->Get('WebPath')%>/Download/CustomFieldValue/<% $value->Id %>/<% $value->Content |un %>"><% $value->Content %></a><br />
+<input type="checkbox" name="<%$NamePrefix%><%$CustomField->Id%>-DeleteValueIds" class="checkbox CF-<%$CustomField->id%>-Edit" value="<% $value->Id %>" /><a href="<%RT->Config->Get('WebPath')%>/Download/CustomFieldValue/<% $value->Id %>/<% $value->Content |un %>"><% $value->Content %></a><br />
% }
% if (!$MaxValues || !$Values || $Values->Count < $MaxValues) {
<input type="file" name="<% $NamePrefix %><% $CustomField->Id %>-Upload" class="CF-<%$CustomField->id%>-Edit" />
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<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" |n,j%>);
- if (basedon != null) {
- var oldchange = basedon.onchange;
- basedon.onchange = function () {
+ var basedon = jQuery('[name^=<% $NamePrefix . $CustomField->BasedOnObj->id %>-Value][type!=hidden]:input:not(.hidden)');
+ basedon.each( function() {
+ var oldchange = jQuery(this).onchange;
+ jQuery(this).change( function () {
+ var vals;
+ if ( jQuery(this).is('select') ) {
+ vals = basedon.first().val();
+ }
+ else {
+ vals = [];
+ jQuery(basedon).each( function() {
+ if ( jQuery(this).is(':checked') ) {
+ vals.push(jQuery(this).val());
+ }
+ });
+ }
filter_cascade(
<% "$id-Values" |n,j%>,
- basedon.value,
+ vals,
1
);
if (oldchange != null)
oldchange();
- };
- basedon.onchange();
+ });
+ });
+
+ if ( basedon.is('select') ) {
+ basedon.change();
+ }
+ else {
+ basedon.first().change();
}
});
--></script>
% }
-% if (@category) {
-%# this hidden select is to supply a full list of values,
-%# see filter_cascade() in js/cascaded.js
- <select name="<%$id%>-Values-Complete" id="<%$id%>-Values-Complete" class="hidden" disabled="disabled">
- <option value=""<% !$selected && qq[ selected="selected"] |n %>><&|/l&>(no value)</&></option>
-% $m->out($out);
- </select>
-% }
% if ( $RenderType eq 'List' ) {
<fieldset class="cfedit">
+<div name="<%$id%>-Values" id="<%$id%>-Values">
% if ( $checktype eq 'radio' ) {
- <input type="<% $checktype %>" name="<% $name %>" id="<% $name %>-none" value="" <% keys %default ? '' : ' checked="checked"' |n%> />
+ <div class="none">
+ <input class="none" type="<% $checktype %>" name="<% $name %>" id="<% $name %>-none" value="" <% keys %default ? '' : ' checked="checked"' |n%> />
<label for="<% $name %>-none"><&|/l&>(no value)</&></label><br />
+ </div>
% }
% my $CFVs = $CustomField->Values;
% while ( my $value = $CFVs->Next ) {
% my $content = $value->Name;
% my $labelid = "$name-". $value->id;
+<div name="<% $value->Category %>">
<input type="<% $checktype %>" name="<% $name %>" id="<% $labelid %>" value="<% $content %>" <% $default{ lc $content }? ' checked="checked"' : '' |n%> />
<label for="<% $labelid %>"><% $content %></label><br />
+</div>
% }
+</div>
</fieldset>
% } else {
+% if (@category) {
+%# this hidden select is to supply a full list of values,
+%# see filter_cascade() in js/cascaded.js
+ <select name="<%$id%>-Values-Complete" id="<%$id%>-Values-Complete" class="hidden" disabled="disabled">
+ <option value=""<% !$selected && qq[ selected="selected"] |n %>><&|/l&>(no value)</&></option>
+% $m->out($out);
+ </select>
+% }
<select
name="<%$id%>-Values" id="<%$id%>-Values" class="CF-<%$CustomField->id%>-Edit"
-% if ( $Rows && ( $Multiple || !@category ) ) {
+% if ( $Rows && ( $Multiple || !@category || $RenderType eq 'Select box') ) {
size="<% $Rows %>"
% }
<% $Multiple && qq[multiple="multiple"] |n %> >
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% }
% if ( $RichText and RT->Config->Get('MessageBoxRichText', $session{'CurrentUser'})) {
- jQuery().ready(function () { ReplaceAllTextareas(<%$DECODED_ARGS->{'CKeditorEncoded'} || 0 |n,j%>) });
+ jQuery().ready(function () { ReplaceAllTextareas() });
% }
--></script>
<%ARGS>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
my @types = (
{
name => "httpurl",
- regex => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#\S+)?/,
+ regex => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#[^\s<]+)?(?<![.?!,;:])/,
action => "url",
},
{
name => "httpurl_overwrite",
- regex => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#\S+)?/,
+ regex => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#[^\s<]+)?(?<![.?!,;:])/,
action => "url_overwrite",
},
);
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% $m->callback( %ARGS, SignatureRef => \$signature );
<% $Default || '' %><% $message %><% $signature %></textarea>
% $m->callback( %ARGS, CallbackName => 'AfterTextArea' );
+% if (RT->Config->Get('MessageBoxRichText', $session{'CurrentUser'})) {
+<input type="text" style="display:none" name="<% $Name %>Type" id="<% $Name %>Type" value="<% $m->request_args->{$Name."Type"}||'' %>" />
+% }
<%INIT>
my $message = '';
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% for my $status (@cur_statuses) {
<td align="right">
- <a href="<% $link_status->($queue, $status) %>"><% $data->{$queue->{id}}->{$status } || '-' %></a>
+ <a href="<% $link_status->($queue, $status) %>"><% $data->{$queue->{id}}->{lc $status} || '-' %></a>
</td>
% }
</tr>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
if ( $lifecycle->IsValid( $status ) ) {
</%perl>
<td align="right">
- <a href="<% $link_status->($queue, $status) %>"><% $data->{$queue->{id}}->{$status } || '-' %></a>
+ <a href="<% $link_status->($queue, $status) %>"><% $data->{$queue->{id}}->{lc $status} || '-' %></a>
</td>
% } else {
<td align="right">-</td>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
$res .= ', ' if $res;
my $id = '';
- $id = $record->Name if $record->can('Name');
+ $id = $record->Name if $record->_Accessible('Name','read');
$id ||= "#". $record->id;
$res .= $id;
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
\'<a href="',
$_->$mode_uri->Resolver->HREF,
\'">',
- ( $_->$mode_uri->IsLocal ? $_->$local_type : $_->$mode ),
+ ( $_->$mode_uri->IsLocal && $_->$local_type ? $_->$local_type : $_->$mode_uri->Resolver->AsString ),
\'</a><br />',
} @{ $_[0]->Links($other_mode,$type)->ItemsArrayRef }
}
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
attribute => 'Lang',
value => sub { return $_[0]->Lang() },
},
+ FreeformContactInfo => {
+ title => 'Extra Info', #loc
+ attribute => 'FreeformContactInfo',
+ value => sub { return $_[0]->FreeformContactInfo() },
+ },
};
</%ONCE>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% }
</select>
% }
-% else {
+% elsif ( $CustomField->Type eq 'Autocomplete' ) {
+<input type="text" id="CF-<% $CustomField->id %>" name="<% $Name %>" size="20" />
+<script type="text/javascript">
+% my @options;
+% my $values = $CustomField->Values;
+% while (my $value = $values->Next) {
+% push @options, {
+% value => $value->Name,
+% label => $value->Description ? $value->Name . ' (' . $value->Description . ')' : $value->Name,
+% };
+% }
+jQuery('#'+'CF-' + <% $CustomField->id %>).autocomplete({ source: <% JSON::to_json(\@options) |n %> });
+</script>
+% } else {
<input name="<%$Name%>" size="20" />
% }
<%args>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
if ( not defined $session{$cache_key} and not $Lite ) {
my $q = RT::Queues->new($session{'CurrentUser'});
$q->UnLimit;
-
+
+ if ( $Default ) {
+ my $d = RT::Queue->new($session{'CurrentUser'});
+ $d->Load($Default);
+ unless ( $d->CurrentUserHasRight('SeeQueue') ) {
+ if ( $d->id ) {
+ push @{$session{$cache_key}{queues}}, {
+ Id => $d->id,
+ Name => '#' . $d->id,
+ Description => '#' . $d->id,
+ };
+ }
+ }
+ }
+
while (my $queue = $q->Next) {
if ($ShowAllQueues || $queue->CurrentUserHasRight($CheckQueueRight)) {
push @{$session{$cache_key}{queues}}, {
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<optgroup label="<% $lifecycle %>">
% }
% foreach my $status (@{$statuses_by_lifecycle{$lifecycle}}) {
-% next if ($SkipDeleted && $status eq 'deleted');
+% next if ($SkipDeleted && lc $status eq 'deleted');
% my $selected = defined $Default && $status eq $Default ? 'selected="selected"' : '';
<option value="<% $status %>" <% $selected |n %>><% loc($status) %></option>
% }
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
$_->{'Format'} =~ s/__(Web(?:Path|Base|BaseURL))__/scalar RT->Config->Get($1)/ge;
# extract-message-catalog would "$1", so we avoid quotes for loc calls
$_->{'Format'} =~ s/__loc\(["']?(\w+)["']?\)__/my $f = "$1"; loc($f)/ge;
- if ( $_->{'Query'} =~ /__Bookmarked__/ ) {
- $_->{'Rows'} = 999;
- }
- elsif ( $_->{'Query'} =~ /__Bookmarks__/ ) {
+ if ( $_->{'Query'} =~ /__Bookmarks__/ ) {
$_->{'Rows'} = 999;
# DEPRECATED: will be here for a while up to 3.10/4.0
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
my $query_string = sub {
my %args = @_;
my $u = URI->new();
- $u->query_form(%args);
+ $u->query_form(map { $_ => $args{$_} } sort keys %args);
return $u->query;
};
if ( $item_map->{$id}->{next} ) {
$search->child( next =>
title => loc('Next') . ' >', class => "nav", path => "/Ticket/Display.html?id=" . $item_map->{$id}->{next});
- $search->child( last =>
- title => loc('Last') . ' >>', class => "nav", path => "/Ticket/Display.html?id=" . $item_map->{last});
+ if ( $item_map->{last} ) {
+ $search->child( last =>
+ title => loc('Last') . ' >>', class => "nav", path => "/Ticket/Display.html?id=" . $item_map->{last});
+ }
}
}
}
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
my @suggestions;
while ( my $group = $groups->Next ) {
- push @suggestions, $group->Name;
+ push @suggestions, { label => $group->Name, value => $group->Name, id => $group->id };
}
</%INIT>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
my $formatted = $m->scomp('/Elements/ShowUser', User => $user, NoEscape => 1);
$formatted =~ s/\n//g;
- my $suggestion = { label => $formatted, value => $user->$return };
+ my $suggestion = { label => $formatted, value => $user->$return, id => $user->id };
$m->callback( CallbackName => "ModifySuggestion", suggestion => $suggestion, user => $user );
push @suggestions, $suggestion;
}
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
$dbh = DBI->connect(
RT::Handle->DSN, $ARGS{DatabaseAdmin}, $ARGS{DatabaseAdminPassword}, { RaiseError => 0, PrintError => 0 },
);
-
+
+ if ( $dbh and $db_type eq "Oracle") {
+ # The database _existing_ is itself insufficient for Oracle -- we need to check for the RT user
+ my $sth = $dbh->prepare('SELECT username FROM dba_users WHERE username = ?');
+ $sth->execute( $ARGS{DatabaseUser} );
+ undef $dbh unless $sth->fetchrow_array;
+
+ push @errors, loc("Oracle users cannot have empty passwords")
+ unless $ARGS{DatabasePassword};
+ }
+
if ( $dbh ) {
# check if table Users exists
eval {
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
+@import "../base/msie6.css";
+
div#body {
top: 0em;
}
#page-navigation ul {
width: auto;
}
+
+.ticket-transaction div.metadata span.actions {
+ right: 1.2em;
+}
+
+div.titlebox,
+#ticket-create-metadata,
+#ticket-update-metadata,
+#ticket-create-message,
+#ticket-update-message {
+ position: relative;
+}
+
+#Ticket-Create-details {
+ float: left;
+ position: relative;
+}
+
+div.submit .extra-buttons {
+ float: none;
+}
+
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
.chart-wrapper .collection-as-table {
width: auto;
font-size: 1em;
+ /* Firefox needs this to let the table flow below the chart on narrow screens */
+ clear: right;
}
.chart-wrapper .collection-as-table th.collection-as-table {
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<![endif]-->
<!--[if lt IE 7]>
-<link rel="stylesheet" href="<%RT->Config->Get('WebPath')%>/NoAuth/css/web2/msie6.css" type="text/css" media="all" />
+<link rel="stylesheet" href="<%RT->Config->Get('WebPath')%>/NoAuth/css/ballard/msie6.css" type="text/css" media="all" />
<![endif]-->
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
+@import "../base/msie6.css";
+
.topaction .select-queue {
margin-top: 0;
}
width: expression(this.width > 401 ? 400 : true);
}
+#page-navigation {
+ width: 100%;
+}
+
+.ticket-transaction div.metadata span.actions {
+ right: 1.1em;
+}
+
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
top: 6.2em;
left: 0em;
z-index: 9997;
- background-color: none;
padding-top: 1em;
padding-right: 0.5em;
padding-left: 0.5em;
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
}
.chart.image {
- margin-top: -1em;
padding-right: 2em;
float: left;
clear: both;
.chart-wrapper .collection-as-table {
width: auto;
font-size: 1em;
+ /* Firefox needs this to let the table flow below the chart on narrow screens */
+ clear: right;
}
.chart-wrapper .collection-as-table th.collection-as-table {
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
-%# 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 }}}
/**
* Farbtastic Color Picker 1.2
* © 2008 Steven Wittens
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
font-family: arial,helvetica,sans-serif !important;
}
+textarea.messagebox, #cke_Content, #cke_UpdateContent {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
+@import "../base/msie6.css";
+
.topaction .select-queue {
margin-top: 0;
}
background-image: url(<%RT->Config->Get('WebPath')%>/NoAuth/css/images/arrows-grey.gif);
}
+.ticket-transaction div.metadata span.actions {
+ right: 1.1em;
+}
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
}
.chart.image {
- margin-top: -1em;
padding-right: 2em;
float: left;
clear: both;
.chart-wrapper .collection-as-table {
width: auto;
font-size: 1em;
+ /* Firefox needs this to let the table flow below the chart on narrow screens */
+ clear: right;
}
.chart-wrapper .collection-as-table th.collection-as-table {
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-function filter_cascade (id, val) {
+function filter_cascade (id, vals) {
+ var element = document.getElementById(id);
+ if (!element) { return };
+
+ if ( element.tagName == 'SELECT' ) {
+ return filter_cascade_select.apply(this, arguments);
+ }
+ else {
+ if ( !( vals instanceof Array ) ) {
+ vals = [vals];
+ }
+
+ if ( arguments.length == 3 && (vals.length == 0 || (vals.length == 1 && vals[0] == '')) ) {
+ // no category, and the category is from a hierchical cf;
+ // leave it empty
+ jQuery(element).find('div').hide();
+ }
+ else {
+ jQuery(element).find('div').hide().find('input').attr('disabled', 'disabled');
+ jQuery(element).find('div[name=]').show().find('input').attr('disabled', '');
+ jQuery(element).find('div.none').show().find('input').attr('disabled','');
+ for ( var j = 0; j < vals.length; j++ ) {
+ jQuery(element).find('div[name^=' + vals[j] + ']').show().find('input').attr('disabled', '');
+ }
+ }
+ }
+}
+
+function filter_cascade_select (id, vals) {
var select = document.getElementById(id);
var complete_select = document.getElementById(id + "-Complete" );
+ if ( !( vals instanceof Array ) ) {
+ vals = [vals];
+ }
if (!select) { return };
var i;
var children = select.childNodes;
if ( complete_select ) {
- while (select.hasChildNodes()){
- select.removeChild(select.firstChild);
- }
+ jQuery(select).children().remove();
var complete_children = complete_select.childNodes;
- if ( val == '' && arguments.length == 3 ) {
- // no category, and the category is from a hierchical cf;
- // leave this set of options empty
- } else if ( val == '' ) {
- // no category, let's clone all node
- for (i in complete_children) {
- if ( complete_children[i].cloneNode ) {
- new_option = complete_children[i].cloneNode(true);
- select.appendChild(new_option);
- }
+ var cloned_labels = {};
+ var cloned_empty_label;
+ for ( var j = 0; j < vals.length; j++ ) {
+ var val = vals[j];
+ if ( val == '' && arguments.length == 3 ) {
+ // no category, and the category is from a hierchical cf;
+ // leave this set of options empty
+ } else if ( val == '' ) {
+ // no category, let's clone all node
+ jQuery(select).append(jQuery(complete_children).clone());
+ break;
}
- }
- else {
- for (i in complete_children) {
- if (!complete_children[i].label ||
- (complete_children[i].hasAttribute &&
- !complete_children[i].hasAttribute('label') ) ||
- complete_children[i].label.substr(0, val.length) == val ) {
- if ( complete_children[i].cloneNode ) {
- new_option = complete_children[i].cloneNode(true);
- select.appendChild(new_option);
+ else {
+ var labels_to_clone = {};
+ for (i = 0; i < complete_children.length; i++) {
+ if (!complete_children[i].label ||
+ (complete_children[i].hasAttribute &&
+ !complete_children[i].hasAttribute('label') ) ) {
+ if ( cloned_empty_label ) {
+ continue;
+ }
}
+ else if ( complete_children[i].label.substr(0, val.length) == val ) {
+ if ( cloned_labels[complete_children[i].label] ) {
+ continue;
+ }
+ labels_to_clone[complete_children[i].label] = true;
+ }
+ else {
+ continue;
+ }
+
+ jQuery(select).append(jQuery(complete_children[i]).clone());
+ }
+
+ if ( !cloned_empty_label )
+ cloned_empty_label = true;
+
+ for ( label in labels_to_clone ) {
+ if ( !cloned_labels[label] )
+ cloned_labels[label] = true;
}
}
}
}
else {
// for back compatibility
- for (i in children) {
+ for (i = 0; i < children.length; i++) {
if (!children[i].label) { continue };
if ( val == '' && arguments.length == 3 ) {
hide(children[i]);
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
if (queryargs.length)
options.source += "?" + queryargs.join("&");
- jQuery(input).autocomplete(options);
+ jQuery(input)
+ .addClass('autocompletes-user')
+ .autocomplete(options)
+ .data("autocomplete")
+ ._renderItem = function(ul, item) {
+ var rendered = jQuery("<a/>");
+
+ if (item.html == null)
+ rendered.text( item.label );
+ else
+ rendered.html( item.html );
+
+ return jQuery("<li/>")
+ .data( "item.autocomplete", item )
+ .append( rendered )
+ .appendTo( ul );
+ };
}
});
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
.replace(/\n/g, "\n<br />");
};
-function ReplaceAllTextareas(encoded) {
+function ReplaceAllTextareas() {
var sAgent = navigator.userAgent.toLowerCase();
if (!CKEDITOR.env.isCompatible ||
sAgent.indexOf('iphone') != -1 ||
var textArea = allTextAreas[i];
if (jQuery(textArea).hasClass("messagebox")) {
// Turn the original plain text content into HTML
- if (encoded == 0) {
+ var type = jQuery("#"+textArea.name+"Type");
+ if (type.val() != "text/html")
textArea.value = textToHTML(textArea.value);
- }
- // For this javascript
- var CKeditorEncoded = document.createElement('input');
- CKeditorEncoded.setAttribute('type', 'hidden');
- CKeditorEncoded.setAttribute('name', 'CKeditorEncoded');
- CKeditorEncoded.setAttribute('value', '1');
- textArea.parentNode.appendChild(CKeditorEncoded);
-
- // For fckeditor
- var typeField = document.createElement('input');
- typeField.setAttribute('type', 'hidden');
- typeField.setAttribute('name', textArea.name + 'Type');
- typeField.setAttribute('value', 'text/html');
- textArea.parentNode.appendChild(typeField);
+ // Set the type
+ type.val("text/html");
CKEDITOR.replace(textArea.name,{width:'100%',height:<% RT->Config->Get('MessageBoxRichTextHeight') |n,j%>});
CKEDITOR.basePath = <%RT->Config->Get('WebPath')|n,j%>+"/NoAuth/RichText/";
// when a value is selected from the autocompleter
function addprincipal_onselect(ev, ui) {
+
+ // if principal link exists, we shall go there instead
+ var principal_link = jQuery(ev.target).closest('form').find('ul.ui-tabs-nav a[href="#acl-' + ui.item.id + '"]:first');
+ if (principal_link.size()) {
+ jQuery(this).val('').blur();
+ update_addprincipal_title( '' ); // reset title to blank for #acl-AddPrincipal
+ principal_link.click();
+ return false;
+ }
+
// pass the item's value along as the title since the input's value
// isn't actually updated yet
toggle_addprincipal_validity(this, true, ui.item.value);
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
}
else {
my ($get, $set, $key, $val, $n, $s);
-
+ my $updated;
foreach $key (keys %data) {
$val = $data{$key};
$key = lc $key;
$k = $changes;
}
}
+ else {
+ $updated ||= 1;
+ }
}
- push(@comments, "# Group $id updated.") unless $n == 0;
+ push(@comments, "# Group $id updated.") if $updated;
}
DONE:
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
}
else {
my ($get, $set, $key, $val, $n, $s);
-
+ my $updated;
foreach $key (keys %data) {
$val = $data{$key};
$key = lc $key;
$k = $changes;
}
}
+ else {
+ $updated ||= 1;
+ }
}
- push(@comments, "# Queue $id updated.") unless $n == 0;
+ push(@comments, "# Queue $id updated.") if $updated;
}
DONE:
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
</%ARGS>
<%INIT>
use MIME::Entity;
-use LWP::MediaTypes;
use RT::Interface::REST;
-use File::Temp qw(tempfile);
-my @tmp_files;
$RT::Logger->debug("Got ticket id=$id for comment");
$RT::Logger->debug("Got args @{[keys(%changes)]}.");
goto OUTPUT;
}
-my $cgi = $m->cgi_object;
my $ent = MIME::Entity->build(
Type => "multipart/mixed",
'X-RT-Interface' => 'REST',
);
-$ent->attach(Data => $changes{Text}) if $changes{Text};
+$ent->attach(
+ 'Content-Type' => $changes{'Content-Type'} || 'text/plain',
+ Data => $changes{Text},
+) if $changes{Text};
-my $i = 1;
-foreach my $att (@atts) {
- local $/=undef;
- my $file = $att;
- $file =~ s#^.*[\\/]##;
- my $fh = $cgi->upload("attachment_$i");
- if ($fh) {
- my $buf;
- my ($w, $tmp) = tempfile();
- my $info = $cgi->uploadInfo($fh);
- push @tmp_files, $tmp;
-
- while (sysread($fh, $buf, 8192)) {
- syswrite($w, $buf);
- }
-
- $ent->attach(
- Path => $tmp,
- Type => $info->{'Content-Type'} || guess_media_type($tmp),
- Filename => $file,
- Disposition => "attachment"
- );
- }
- else {
+{
+ my ($status, $msg) = process_attachments($ent, @atts);
+ unless ( $status ) {
$e = 1;
- $c = "# No attachment for $att.";
+ $c = "# $msg";
goto OUTPUT;
}
-
- $i++;
}
unless ($ticket->CurrentUserHasRight('ModifyTicket') ||
OUTPUT:
-unlink @tmp_files;
return [ $c, $o, $k, $e ];
</%INIT>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
my @people = qw(Requestors Cc AdminCc);
my @create = qw(Queue Requestor Subject Cc AdminCc Owner Status Priority
InitialPriority FinalPriority TimeEstimated TimeWorked
- TimeLeft Starts Started Due Resolved);
+ TimeLeft Starts Started Due Resolved Content-Type);
my @simple = qw(Subject Status Priority Disabled TimeEstimated TimeWorked
TimeLeft InitialPriority FinalPriority);
my %dates = map {lc $_ => $_} @dates;
return [ "# Ticket $id does not exist.", [], {}, 1 ];
}
elsif ( %data ) {
- if ( $data{status} && $data{status} eq 'deleted' && ! grep { $_ ne 'id' && $_ ne 'status' } keys %data ) {
+ if ( $data{status} && lc $data{status} eq 'deleted' && ! grep { $_ ne 'id' && $_ ne 'status' } keys %data ) {
if ( !$ticket->CurrentUserHasRight('DeleteTicket') ) {
return [ "# You are not allowed to delete ticket $id.", [], {}, 1 ];
}
return [
"# Required: id, Queue",
[ qw(id Queue Requestor Subject Cc AdminCc Owner Status Priority
- InitialPriority FinalPriority TimeEstimated Starts Due Text) ],
+ InitialPriority FinalPriority TimeEstimated Starts Due Attachment Text) ],
{
id => "ticket/new",
Queue => $queue->Name,
TimeEstimated => 0,
Starts => $starts->ISO,
Due => $due->ISO,
+ Attachment => '',
Text => "",
},
0
else {
# We'll create a new ticket, and fall through to set fields that
# can't be set in the call to Create().
- my (%v, $text);
+ my (%v, $text, @atts);
foreach my $k (keys %data) {
# flexibly parse any dates
elsif (lc $k eq 'text') {
$text = delete $data{$k};
}
+ elsif (lc $k eq 'attachment') {
+ push @atts, @{ vsplit(delete $data{$k}) };
+ }
elsif ( $k !~ /^(?:id|requestors)$/i ) {
$e = 1;
push @$o, $k;
# people fields allow multiple values
$v{$_} = vsplit($v{$_}) foreach ( grep $create{lc $_}, @people );
- if ($text) {
+ if ($text || @atts) {
$v{MIMEObj} =
MIME::Entity->build(
+ Type => "multipart/mixed",
From => $session{CurrentUser}->EmailAddress,
Subject => $v{Subject},
- Data => $text,
'X-RT-Interface' => 'REST',
);
+ $v{MIMEObj}->attach(
+ Data => $text,
+ 'Content-Type' => $v{'Content-Type'} || 'text/plain',
+ ) if $text;
+ my ($status, $msg) = process_attachments($v{'MIMEObj'}, @atts);
+ unless ($status) {
+ push(@comments, "# $msg");
+ goto DONE;
+ }
+ $v{MIMEObj}->make_singlepart;
}
my($tid,$trid,$terr) = $ticket->Create(%v);
}
else {
my ($get, $set, $key, $val, $n, $s);
+ my $updated;
foreach $key (keys %data) {
$val = $data{$key};
$s =~ s/\\'/'/g;
push @new, $s;
}
- elsif ( $a =~ /^q{/ ) {
+ elsif ( $a =~ /^q\{/ ) {
my $s = $a;
- while ( $a !~ /}$/ ) {
+ while ( $a !~ /\}$/ ) {
( $a, $b ) = split /\s*,\s*/, $b, 2;
$s .= ',' . $a;
}
- $s =~ s/^q{//;
- $s =~ s/}//;
+ $s =~ s/^q\{//;
+ $s =~ s/\}//;
push @new, $s;
}
else {
}
}
}
- elsif ($key ne 'id' && $key ne 'type' && $key ne 'creator') {
+ elsif ($key ne 'id' && $key ne 'type' && $key ne 'creator' && $key ne 'content-type' ) {
$n = 0;
$s = "Unknown field.";
}
$k = $changes;
}
}
+ else {
+ $updated ||= 1;
+ }
}
- push(@comments, "# Ticket ".$ticket->id." updated.") unless $n == 0;
+ push(@comments, "# Ticket ".$ticket->id." updated.") if $updated;
}
DONE:
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
}
else {
my ($get, $set, $key, $val, $n, $s);
-
+ my $updated;
foreach $key (keys %data) {
$val = $data{$key};
$key = lc $key;
$k = $changes;
}
}
+ else {
+ $updated ||= 1;
+ }
}
- push(@comments, "# User $id updated.") unless $n == 0;
+ push(@comments, "# User $id updated.") if $updated;
}
DONE:
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
</%ARGS>
<%INIT>
use MIME::Entity;
-use LWP::MediaTypes;
use RT::Interface::REST;
-use File::Temp qw(tempfile);
-my @tmp_files;
my $ticket = RT::Ticket->new($session{CurrentUser});
my $object = $r->path_info;
);
$ent->attach(Data => $k->{Text}) if $k->{Text};
-my $i = 1;
-foreach my $att (@atts) {
- local $/=undef;
- my $file = $att;
- $file =~ s#^.*[\\/]##;
-
- my $fh = $cgi->upload("attachment_$i");
- if ($fh) {
- my $buf;
- my ($w, $tmp) = tempfile();
- push @tmp_files, $tmp;
- my $info = $cgi->uploadInfo();
-
- while (sysread($fh, $buf, 8192)) {
- syswrite($w, $buf);
- }
-
- $ent->attach(
- Path => $tmp,
- Type => $info->{'Content-Type'} || guess_media_type($tmp),
- Filename => $file,
- Disposition => "attachment"
- );
- }
- else {
+{
+ my ($res, $msg) = process_attachments($ent, @atts);
+ unless ( $res ) {
$status = "400 Bad Request";
- $output = "No attachment for $att.\n";
+ $output = "$msg\n";
goto OUTPUT;
}
-
- $i++;
}
$ticket->Load($object);
OUTPUT:
-unlink @tmp_files;
</%INIT>
RT/<% $RT::VERSION %> <% $status %>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
# Try to find if we're adding a clause
foreach my $arg ( keys %ARGS ) {
- next unless $arg =~ m/^ValueOf([\w\.]+|'CF.{.*?}')$/
+ next unless $arg =~ m/^ValueOf([\w\.]+|'\w*CF\.\{.*?\}')$/
&& ( ref $ARGS{$arg} eq "ARRAY"
? grep $_ ne '', @{ $ARGS{$arg} }
: $ARGS{$arg} ne '' );
$value = "'$value'";
}
- if ($keyword =~ /^'CF\.{(.*)}'/) {
- my $cf = $1;
+ if ($keyword =~ /^'(\w*CF)\.\{(.*)\}'/) {
+ my ($field, $cf) = ($1, $2);
$cf =~ s/(['\\])/\\$1/g;
- $keyword = "'CF.{$cf}'";
+ $keyword = "'$field.{$cf}'";
}
my $clause = {
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
#Iterate through each ticket we've been handed
my @linkresults;
-my %queues;
$Tickets->RedoSearch();
#Update the links
$ARGS{'id'} = $Ticket->id;
- $queues{ $Ticket->QueueObj->Id }++;
my @updateresults = ProcessUpdateMessage(
TicketObj => $Ticket,
my $TxnCFs = RT::CustomFields->new( $session{CurrentUser} );
$TxnCFs->LimitToLookupType( RT::Transaction->CustomFieldLookupType );
-$TxnCFs->LimitToGlobalOrObjectId( sort keys %queues );
+$TxnCFs->LimitToGlobalOrObjectId( keys %$seen_queues );
</%INIT>
<%args>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
$Description => undef
</%args>
<%init>
+$m->callback( ARGSRef => \%ARGS, CallbackName => 'Initial' );
+
$ARGS{Query} ||= 'id > 0';
# FIXME: should be factored with RT::Report::Tickets::Label :(
my $PrimaryGroupByLabel;
-if ( $PrimaryGroupBy =~ /^(?:CF|CustomField)\.{(.*)}$/ ) {
+if ( $PrimaryGroupBy =~ /^(?:CF|CustomField)\.\{(.*)\}$/ ) {
my $cf = $1;
if ( $cf =~ /\D/ ) {
$PrimaryGroupByLabel = loc( "custom field '[_1]'", $cf );
);
for(@session_fields) {
- $query{$_} = $current->{$_} unless defined $query{$_};
$query{$_} = $DECODED_ARGS->{$_} unless defined $query{$_};
+ $query{$_} = $current->{$_} unless defined $query{$_};
}
if ($DECODED_ARGS->{'SavedSearchLoadSubmit'}) {
}
+$m->callback( ARGSRef => \%ARGS, QueryArgsRef => \%query );
</%init>
<& /Elements/Header, Title => $title &>
<& /Elements/Tabs, QueryArgs => \%query &>
<& /Elements/ListActions, actions => \@actions &>
+
+% $m->callback( ARGSRef => \%ARGS, CallbackName => 'BeforeChart' );
+
<& /Search/Elements/Chart, %ARGS &>
+% $m->callback( ARGSRef => \%ARGS, CallbackName => 'AfterChart' );
+
<div class="chart-meta">
<div class="chart-type">
<&| /Widgets/TitleBox, title => loc('Chart Properties')&>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
my ( @seen);
$Format ||= RT->Config->Get('DefaultSearchResultFormat');
-my @format = split( /,\s*/, $Format );
+my @format = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $Format);
foreach my $field (@format) {
- my %column = ();
- $field =~ s/'(.*)'/$1/;
- my ( $prefix, $suffix );
- if ( $field =~ m/(.*)__(.*)__(.*)/ ) {
- $prefix = $1;
- $suffix = $3;
- $field = $2;
- }
- $field = "<blank>" if !$field;
- $column{Prefix} = $prefix;
- $column{Suffix} = $suffix;
- $field =~ s/\s*(.*)\s*/$1/;
- $column{Column} = $field;
- push @seen, \%column;
+ # "title" is for columns like NEWLINE, which doesn't have "attribute"
+ $field->{Column} = $field->{attribute} || $field->{title} || '<blank>';
+ push @seen, $field;
}
if ( $RemoveCol ) {
my @format_string;
foreach my $field (@seen) {
next unless $field;
- my $row = "'";
- $row .= $field->{'Prefix'} if defined $field->{'Prefix'};
- $row .= "__$field->{'Column'}__"
- unless ( $field->{'Column'} eq "<blank>" );
- $row .= $field->{'Suffix'} if defined $field->{'Suffix'};
- $row .= "'";
+ my $row = "";
+ if ( $field->{'output'} ) {
+ $row = join '', @{$field->{'output'}};
+ }
+ else {
+ $row .= $field->{'Prefix'} if defined $field->{'Prefix'};
+ $row .= "__$field->{'Column'}__"
+ unless ( $field->{'Column'} eq "<blank>" );
+ $row .= $field->{'Suffix'} if defined $field->{'Suffix'};
+ }
+ $row =~ s!([\\'])!\\$1!g;
+ $row = "'$row'";
push( @format_string, $row );
}
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<& ConditionRow, Condition => $_ &>
% }
<%INIT>
-my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
-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);
- $CustomFields->LimitToQueue($queue->Id) if $queue->Id;
-}
-$CustomFields->LimitToGlobal;
$m->callback(
CallbackName => 'MassageCustomFields',
CustomFields => $CustomFields,
my @lines;
while ( my $CustomField = $CustomFields->Next ) {
my %line;
- $line{'Name'} = "'CF.{" . $CustomField->Name . "}'";
+ $line{'Name'} = "'$TicketSQLField.{" . $CustomField->Name . "}'";
$line{'Field'} = $CustomField->Name;
# Op
<%ARGS>
%queues => ()
+$CustomFields
+$TicketSQLField => 'CF'
</%ARGS>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<table width="100%" cellspacing="0" cellpadding="0" border="0">
-
+% $m->callback( %ARGS, CallbackName => "BeforeBasics" );
<& PickBasics &>
<& PickCustomerFields &>
-<& PickCFs, queues => \%queues &>
+<& PickTicketCFs, queues => \%queues &>
+% $m->callback( %ARGS, CallbackName => "AfterCFs" );
<tr class="separator"><td colspan="3"><hr /></td></tr>
<tr>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% $m->callback( ARGSRef => \%ARGS, CallbackName => 'BeforeResults' );
+% unless ($ok) {
+% $msg =~ s{ at .*? line .*}{}s;
+<&| /Widgets/TitleBox, title => loc("Error"), class => "error" &>
+<&|/l_unsafe, "<i>".$m->interp->apply_escapes($msg, "h")."</i>" &>There was an error parsing your search query: [_1]. Your RT admin can find more information in the error logs.</&>
+</&>
+% } else {
+
<& /Elements/CollectionList,
Query => $Query,
TotalFound => $ticketcount,
BaseURL => $BaseURL
&>
+% }
% $m->callback( ARGSRef => \%ARGS, CallbackName => 'AfterResults' );
% my %hiddens = (Query => $Query, Format => $Format, Rows => $Rows, OrderBy => $OrderBy, Order => $Order, HideResults => $HideResults, Page => $Page, SavedChartSearchId => $SavedChartSearchId );
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% my @strong = qw(<strong> </strong>);
-<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_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]. Searching for [_1]@domainname.com[_2] will return tickets with requestors from that domain.</&></p>
<p><&|/l&>Any word not recognized by RT is searched for in ticket subjects.</&></p>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% $m->callback(CallbackName => 'BeforeActionList', %ARGS, Actions => \@results, ARGSRef => \%ARGS, Ticket => $Ticket );
<& /Elements/ListActions, actions => \@results &>
+<& /Ticket/Elements/ShowUpdateStatus, Ticket => $Ticket &>
<table width="100%" class="ticketsummary" >
<tr>
$m->abort();
}
- if (@results) {
- # We've done something, so we need to clear the decks to avoid
- # resubmission on refresh.
- # But we need to store Actions somewhere too, so we don't lose them.
- my $key = Digest::MD5::md5_hex(rand(1024));
- push @{ $session{"Actions"}->{$key} ||= [] }, @results;
- $session{'i'}++;
- RT::Interface::Web::Redirect( RT->Config->Get('WebURL') ."SelfService/Display.html?id=". $Ticket->id."&results=".$key);
+ if ( $ARGS{'MarkAsSeen'} ) {
+ $Ticket->SetAttribute(
+ Name => 'User-'. $Ticket->CurrentUser->id .'-SeenUpTo',
+ Content => $Ticket->LastUpdated,
+ );
+ push @results, loc('Marked all messages as seen');
}
+ # This code does automatic redirection if any updates happen.
+ MaybeRedirectForResults(
+ Actions => \@results,
+ Path => '/SelfService/Display.html',
+ Anchor => $ARGS{'Anchor'},
+ Arguments => { id => $Ticket->id },
+ );
+
my $Transactions = $Ticket->Transactions;
my $attachments =
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
while ( my $cf_value = $cf_values->Next ) {
push @cf_values, $cf_value->Content;
}
- $clone->{"Object-RT::Ticket--CustomField-$cf_id-Value"} = join "\n",
- @cf_values;
+
+ if ( @cf_values > 1 && $cf->Type eq 'Select' ) {
+ $clone->{"Object-RT::Ticket--CustomField-$cf_id-Value"} = \@cf_values;
+ }
+ else {
+ $clone->{"Object-RT::Ticket--CustomField-$cf_id-Value"} = join "\n",
+ @cf_values;
+ }
}
# Pass customer links along (even though cloning of parent links
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
$link_rel{first} = "Ticket/Display.html?id=" . $item_map->{first} if $item_map->{$TicketObj->Id}{prev};
$link_rel{prev} = "Ticket/Display.html?id=" . $item_map->{$TicketObj->Id}{prev} if $item_map->{$TicketObj->Id}{prev};
$link_rel{next} = "Ticket/Display.html?id=" . $item_map->{$TicketObj->Id}{next} if $item_map->{$TicketObj->Id}{next};
- $link_rel{last} = "Ticket/Display.html?id=" . $item_map->{last} if $item_map->{$TicketObj->Id}{next};
+ $link_rel{last} = "Ticket/Display.html?id=" . $item_map->{last} if $item_map->{$TicketObj->Id}{next} && $item_map->{last};
}
</%INIT>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<tr><td class="label"><&|/l&>Attached file</&>:</td>
<td>
<&|/l&>Check box to delete</&><br />
-% foreach my $attach_name (keys %{$session{'Attachments'}}) {
+% foreach my $attach_name (sort keys %{$session{'Attachments'}}) {
<input type="checkbox" class="checkbox" name="DeleteAttach-<%$attach_name%>" value="1" /><%$attach_name%><br />
% } # end of foreach
</td>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<%INIT>
my $WillResolveObj = $TicketObj->WillResolveObj if defined $TicketObj;
unless ( @fields ) {
+ my $subject = $defaults{'Subject'} || $TicketObj->Subject;
@fields = (
{ name => 'Subject',
- html => '<input name="Subject" value="'.$m->interp->apply_escapes( $defaults{'Subject'} || $TicketObj->Subject, 'h' ).'" />',
+ html => '<input name="Subject" value="'.(defined($subject) ? $m->interp->apply_escapes( $subject, 'h' ) : '').'" />',
},
{ name => 'Status',
comp => '/Elements/SelectStatus',
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
% }
% }
-% if ($Ticket->Status ne "deleted") {
+% if (lc $Ticket->Status ne "deleted") {
<&|/l&>New reminder:</&>
<& SELF:NewReminder, Ticket => $Ticket &>
% }
-% return($Ticket->Status ne "deleted" or $visible);
+% return(lc $Ticket->Status ne "deleted" or $visible);
<%method NewReminder>
<%args>
$Ticket
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
&><& /Ticket/Elements/ShowCustomers, Ticket => $Ticket &></&>
<&| /Widgets/TitleBox, title => loc('People'),
- (($can_modify || $can_modify_owner) ? (title_href => RT->Config->Get('WebPath')."/Ticket/ModifyPeople.html?id=".$Ticket->Id) : ()),
+ (($can_modify || $can_modify_owner || $can_modify_people) ? (title_href => RT->Config->Get('WebPath')."/Ticket/ModifyPeople.html?id=".$Ticket->Id) : ()),
class => 'ticket-info-people',
&><& /Ticket/Elements/ShowPeople, Ticket => $Ticket &></&>
<& /Ticket/Elements/ShowAttachments, Ticket => $Ticket, Attachments => $Attachments &>
my $can_modify_owner = $Ticket->CurrentUserHasRight('OwnTicket')
|| $Ticket->CurrentUserHasRight('TakeTicket')
|| $Ticket->CurrentUserHasRight('StealTicket');
+my $can_modify_people = $Ticket->CurrentUserHasRight('Watch')
+ || $Ticket->CurrentUserHasRight('WatchAsAdminCc');
</%INIT>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
ticket => $Ticket,
);
- require HTML::Quoted;
- $content = HTML::Quoted->extract($content) unless length $name;
+ unless (length $name) {
+ eval {
+ require HTML::Quoted;
+ $content = HTML::Quoted->extract($content)
+ };
+ if ($@) {
+ RT->Logger->error(
+ "HTML::Quoted couldn't process attachment #@{[$message->id]}: $@."
+ . " This is a bug, please report it to rt-bugs\@bestpractical.com.");
+ }
+ }
$m->comp(
'ShowMessageStanza',
# It's a text type we don't have special handling for
else {
unless ( length $name ) {
- eval { require Text::Quoted; $content = Text::Quoted::extract($content); };
- if ($@) { $RT::Logger->warning( "Text::Quoted failed: $@" ) }
+ eval {
+ require Text::Quoted;
+ # XXX: Deprecate ->can check in 4.2 and simply bump version requirement.
+ Text::Quoted::set_quote_characters(undef) # only use >
+ if Text::Quoted->can("set_quote_characters");
+ $content = Text::Quoted::extract($content);
+ };
+ if ($@) {
+ RT->Logger->error(
+ "Text::Quoted couldn't process attachment #@{[$message->id]}: $@."
+ . " This is a bug, please report it to rt-bugs\@bestpractical.com.");
+ }
}
$m->comp(
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<&| /Widgets/TitleBox, title => loc('New messages'), title_href => "#txn-". $txn->id &>
<&|/l&>There are unread messages on this ticket.</&>
<&|/l,
- RT->Config->Get('WebPath') ."/Ticket/Display.html?id=". $Ticket->id. "#txn-".$txn->id,
- RT->Config->Get('WebPath') ."/Ticket/Display.html?id=". $Ticket->id ."&MarkAsSeen=1&Anchor=txn-" . $txn->id
+ RT->Config->Get('WebPath') ."/$DisplayPath/Display.html?id=". $Ticket->id. "#txn-".$txn->id,
+ RT->Config->Get('WebPath') ."/$DisplayPath/Display.html?id=". $Ticket->id ."&MarkAsSeen=1&Anchor=txn-" . $txn->id
&>You can <a href="[_1]">jump to the first unread message</a> or <a href="[_2]">jump to the first unread message and mark all messages as seen</a>.</&>
</&>
</div>
<%INIT>
return unless (RT->Config->Get( 'ShowUnreadMessageNotifications', $session{'CurrentUser'}));
my $txn = $Ticket->SeenUpTo or return;
+
+my $DisplayPath = $session{'CurrentUser'}->Privileged ? 'Ticket' : 'SelfService';
</%INIT>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
</table>
</&>
+% $m->callback( %ARGS, CallbackName => 'BeforeSubmit', Ticket => $TicketObj );
+
<& /Elements/Submit, Label => loc('Update Ticket'), Name => 'SubmitTicket', id => 'SubmitTicket' &>
+% $m->callback( %ARGS, CallbackName => 'BeforeScrips', Ticket => $TicketObj );
+
% if ($TicketObj->CurrentUserHasRight('ShowOutgoingEmail')) {
<&|/Widgets/TitleBox, title => loc('Scrips and Recipients'), id => 'previewscrips', rolledup => RT->Config->Get('SimplifiedRecipients', $session{'CurrentUser'}) &>
<& /Ticket/Elements/PreviewScrips, TicketObj => $TicketObj, %ARGS &>
% }
</div>
+% $m->callback( %ARGS, CallbackName => 'AfterScrips', Ticket => $TicketObj );
+
% if (my $recips = $m->notes("DryRun-Recipients-".$TicketObj->Id)) {
<input type="hidden" name="TxnRecipients" value="<% join ",",sort keys %{$recips} %>" />
% }
</form>
<hr class="clear" />
+
+% $m->callback( %ARGS, CallbackName => 'AfterForm', Ticket => $TicketObj );
+
<%INIT>
my $CanRespond = 0;
my $CanComment = 0;
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
<& /Elements/Logo, ShowName => 1, OnlyCustom => 1 &>
<div id="bpscredits">
<div id="copyright">
-<&|/l_unsafe, '', '', '2013', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>', &>[_1] RT [_2] Copyright 1996-[_3] [_4].</&>
+<&|/l_unsafe, '', '', '2014', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>', &>[_1] RT [_2] Copyright 1996-[_3] [_4].</&>
</div>
</div>
</body>
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%# COPYRIGHT:
%#
-%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
%# <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
'global custom field was set');
is ($dependson->FirstCustomFieldValue('QueueCF'), 'Another Value',
'queue custom field was set');
-unlike ($dependson->Subject, qr/{/, "The subject doesn't have braces in it. that means we're interpreting expressions");
+unlike ($dependson->Subject, qr/\{/, "The subject doesn't have braces in it. that means we're interpreting expressions");
is ($t->ReferredToBy->Count,1, "It's only referred to by one other ticket");
is ($t->ReferredToBy->First->BaseObj->Id,$t->DependsOn->First->TargetObj->Id, "The same ticket that depends on it refers to it.");
use RT::Action::CreateTickets;
'1970-01-01T00:00:00Z',
"W3CDTF format with defaults");
is($date->Get(Format =>'RFC2822'),
- 'Thu, 1 Jan 1970 00:00:00 +0000',
+ 'Thu, 01 Jan 1970 00:00:00 +0000',
"RFC2822 format with defaults");
is($date->Get(Format =>'LocalizedDateTime'),
'Thu, Jan 1, 1970 12:00:00 AM',
'1970-01-01',
"W3CDTF format without time part");
is($date->RFC2822(Time => 0),
- 'Thu, 1 Jan 1970',
+ 'Thu, 01 Jan 1970',
"RFC2822 format without time part");
is($date->LocalizedDateTime(Time => 0),
'Thu, Jan 1, 1970',
"RFC2822 format without date part and seconds");
is($date->RFC2822(DayOfWeek => 0),
- '1 Jan 1970 00:00:00 +0000',
+ '01 Jan 1970 00:00:00 +0000',
"RFC2822 format without 'day of week' part");
is($date->RFC2822(DayOfWeek => 0, Date => 0),
'00:00:00 +0000',
'1970-01-01',
"'Date' method, W3CDTF format");
is($date->Date(Format => 'RFC2822'),
- 'Thu, 1 Jan 1970',
+ 'Thu, 01 Jan 1970',
"'Date' method, RFC2822 format");
is($date->Date(Time => 1),
'1970-01-01',
'1970-01-01T00:00:00Z',
"'DateTime' method, W3CDTF format");
is($date->DateTime(Format =>'RFC2822'),
- 'Thu, 1 Jan 1970 00:00:00 +0000',
+ 'Thu, 01 Jan 1970 00:00:00 +0000',
"'DateTime' method, RFC2822 format");
is($date->DateTime(Date => 0, Time => 0),
'1970-01-01 00:00:00',
$date->Set( Format => 'ISO', Timezone => 'utc', Value => '2005-01-01 15:10:00' );
is($date->ISO( Timezone => 'user' ), '2005-01-01 18:10:00', "ISO");
is($date->W3CDTF( Timezone => 'user' ), '2005-01-01T18:10:00+03:00', "W3C DTF");
- is($date->RFC2822( Timezone => 'user' ), 'Sat, 1 Jan 2005 18:10:00 +0300', "RFC2822");
+ is($date->RFC2822( Timezone => 'user' ), 'Sat, 01 Jan 2005 18:10:00 +0300', "RFC2822");
# DST
$date = RT::Date->new( $current_user );
$date->Set( Format => 'ISO', Timezone => 'utc', Value => '2005-07-01 15:10:00' );
is($date->ISO( Timezone => 'user' ), '2005-07-01 19:10:00', "ISO");
is($date->W3CDTF( Timezone => 'user' ), '2005-07-01T19:10:00+04:00', "W3C DTF");
- is($date->RFC2822( Timezone => 'user' ), 'Fri, 1 Jul 2005 19:10:00 +0400', "RFC2822");
+ is($date->RFC2822( Timezone => 'user' ), 'Fri, 01 Jul 2005 19:10:00 +0400', "RFC2822");
}
{ # negative timezone
$date->Set( Format => 'ISO', Timezone => 'utc', Value => '2005-01-01 15:10:00' );
is($date->ISO( Timezone => 'user' ), '2005-01-01 10:10:00', "ISO");
is($date->W3CDTF( Timezone => 'user' ), '2005-01-01T10:10:00-05:00', "W3C DTF");
- is($date->RFC2822( Timezone => 'user' ), 'Sat, 1 Jan 2005 10:10:00 -0500', "RFC2822");
+ is($date->RFC2822( Timezone => 'user' ), 'Sat, 01 Jan 2005 10:10:00 -0500', "RFC2822");
# DST
$date = RT::Date->new( $current_user );
$date->Set( Format => 'ISO', Timezone => 'utc', Value => '2005-07-01 15:10:00' );
is($date->ISO( Timezone => 'user' ), '2005-07-01 11:10:00', "ISO");
is($date->W3CDTF( Timezone => 'user' ), '2005-07-01T11:10:00-04:00', "W3C DTF");
- is($date->RFC2822( Timezone => 'user' ), 'Fri, 1 Jul 2005 11:10:00 -0400', "RFC2822");
+ is($date->RFC2822( Timezone => 'user' ), 'Fri, 01 Jul 2005 11:10:00 -0400', "RFC2822");
}
warning_like
{ # set+datemanip format(Time::ParseDate)
my $date = RT::Date->new(RT->SystemUser);
- $date->Set(Format => 'unknown', Value => 'weird date');
- is($date->Unix, 0, "date was wrong");
RT->Config->Set( Timezone => 'Europe/Moscow' );
$date->Set(Format => 'datemanip', Value => '2005-11-28 15:10:00');
{ # set+unknown format(Time::ParseDate)
my $date = RT::Date->new(RT->SystemUser);
- $date->Set(Format => 'unknown', Value => 'weird date');
+ warnings_like {
+ $date->Set(Format => 'unknown', Value => 'weird date');
+ } qr{Couldn't parse date 'weird date' by Time::ParseDate};
is($date->Unix, 0, "date was wrong");
RT->Config->Set( Timezone => 'Europe/Moscow' );
RT->Config->Set( DateTimeFormat => 'RFC2822' );
$date->Unix(1);
- is($date->AsString, 'Thu, 1 Jan 1970 00:00:01 +0000', "correct string");
+ is($date->AsString, 'Thu, 01 Jan 1970 00:00:01 +0000', "correct string");
RT->Config->Set( DateTimeFormat => { Format => 'RFC2822', Seconds => 0 } );
$date->Unix(1);
- is($date->AsString, 'Thu, 1 Jan 1970 00:00 +0000', "correct string");
- is($date->AsString(Seconds => 1), 'Thu, 1 Jan 1970 00:00:01 +0000', "correct string");
+ is($date->AsString, 'Thu, 01 Jan 1970 00:00 +0000', "correct string");
+ is($date->AsString(Seconds => 1), 'Thu, 01 Jan 1970 00:00:01 +0000', "correct string");
}
{ # DurationAsString
is($ng->id , $group->id, "Loaded the right group");
-ok (($id,$msg) = $ng->AddMember('1'), "Added a member to the group");
+my @users = (undef);
+for my $number (1..3) {
+ my $user = RT::User->new(RT->SystemUser);
+ $user->Create( Name => "User $number" );
+ push @users, $user->id;
+}
+
+
+ok (($id,$msg) = $ng->AddMember( $users[1] ), "Added a member to the group");
ok($id, $msg);
-ok (($id,$msg) = $ng->AddMember('2' ), "Added a member to the group");
+ok (($id,$msg) = $ng->AddMember( $users[2] ), "Added a member to the group");
ok($id, $msg);
-ok (($id,$msg) = $ng->AddMember('3' ), "Added a member to the group");
+ok (($id,$msg) = $ng->AddMember( $users[3] ), "Added a member to the group");
ok($id, $msg);
# Group 1 now has members 1, 2 ,3
isnt ($id_2 , 0, "Created group 2 ok- $msg_2 ");
ok (($id,$msg) = $group_2->AddMember($ng->PrincipalId), "Made TestGroup a member of testgroup2");
ok($id, $msg);
-ok (($id,$msg) = $group_2->AddMember('1' ), "Added member RT_System to the group TestGroup2");
+ok (($id,$msg) = $group_2->AddMember( $users[1] ), "Added member User 1 to the group TestGroup2");
ok($id, $msg);
# Group 2 how has 1, g1->{1, 2,3}
# g3 now has g2->{1, g1->{1,2,3}}
my $principal_1 = RT::Principal->new(RT->SystemUser);
-$principal_1->Load('1');
+$principal_1->Load( $users[1] );
my $principal_2 = RT::Principal->new(RT->SystemUser);
-$principal_2->Load('2');
+$principal_2->Load( $users[2] );
-ok (($id,$msg) = $group_3->AddMember('1' ), "Added member RT_System to the group TestGroup2");
+ok (($id,$msg) = $group_3->AddMember( $users[1] ), "Added member User 1 to the group TestGroup2");
ok($id, $msg);
# g3 now has 1, g2->{1, g1->{1,2,3}}
-use RT::Test nodata => 1, tests => 30;
+use RT::Test nodata => 1, tests => 38;
use strict;
use warnings;
+use Test::Warn;
+
+sub reset_rights { RT::Test->set_rights }
+
# clear all global right
-{
- my $acl = RT::ACL->new(RT->SystemUser);
- $acl->Limit( FIELD => 'RightName', OPERATOR => '!=', VALUE => 'SuperUser' );
- $acl->LimitToObject( $RT::System );
- while( my $ace = $acl->Next ) {
- $ace->Delete;
- }
-}
+reset_rights;
my $queue = RT::Test->load_or_create_queue( Name => 'Regression' );
ok $queue && $queue->id, 'loaded or created queue';
"user is not AdminCc and can't modify ticket2 (same question different answer)"
);
}
+
+my $queue2 = RT::Test->load_or_create_queue( Name => 'Rights' );
+ok $queue2 && $queue2->id, 'loaded or created queue';
+
+my $user2 = RT::Test->load_or_create_user(
+ Name => 'user2', Password => 'password',
+);
+ok $user2 && $user2->id, 'Created user: ' . $user2->Name . ' with id ' . $user2->Id;
+
+warning_like {
+ ok( !$user2->HasRight( Right => 'Foo', Object => $queue2 ),
+ "HasRight false for invalid right Foo"
+ );
+} qr/Invalid right\. Couldn't canonicalize right 'Foo'/,
+ 'Got warning on invalid right';
+
+
+note "Right name canonicalization";
+{
+ reset_rights;
+ my ($ok, $msg) = $user->PrincipalObj->GrantRight(
+ Right => "showticket",
+ Object => RT->System,
+ );
+ ok $ok, "Granted showticket: $msg";
+ ok $user->HasRight( Right => "ShowTicket", Object => RT->System ), "HasRight ShowTicket";
+
+ reset_rights;
+ ($ok, $msg) = $user->PrincipalObj->GrantRight(
+ Right => "ShowTicket",
+ Object => RT->System,
+ );
+ ok $ok, "Granted ShowTicket: $msg";
+ ok $user->HasRight( Right => "showticket", Object => RT->System ), "HasRight showticket";
+}
}
sub have_no_rights {
- $SIG{'INT'} = $SIG{'TERM'} = sub { print STDERR Carp::longmess('boo'); exit 1 };
local $Test::Builder::Level = $Test::Builder::Level + 1;
foreach my $u ( @_ ) {
foreach my $q (
use strict;
use warnings;
use RT;
-use RT::Test tests => 11;
+use RT::Test tests => 19;
{
}
+#20767: CleanSlate doesn't clear RT::SearchBuilder's flags for handling Disabled columns
+{
+ my $items;
+
+ ok(my $queues = RT::Queues->new(RT->SystemUser), 'Created a queues object');
+ ok( $queues->UnLimit(),'Unlimited the result set of the queues object');
+
+ # sanity check
+ is( $queues->{'handled_disabled_column'} => undef, 'handled_disabled_column IS NOT set' );
+ is( $queues->{'find_disabled_rows'} => undef, 'find_disabled_rows IS NOT set ' );
+
+ $queues->LimitToDeleted;
+
+ # sanity check
+ ok( $queues->{'handled_disabled_column'}, 'handled_disabled_column IS set' );
+ ok( $queues->{'find_disabled_rows'}, 'find_disabled_rows IS set ' );
+
+ $queues->CleanSlate;
+
+ # these fail without the overloaded CleanSlate method
+ is( $queues->{'handled_disabled_column'} => undef, 'handled_disabled_column IS NOT set' );
+ is( $queues->{'find_disabled_rows'} => undef, 'find_disabled_rows IS NOT set ' );
+}
+
mkdir $tmp;
my $sphinx_conf = $output;
- $sphinx_conf =~ s/.*?source rt {/source rt {/ms;
+ $sphinx_conf =~ s/.*?source rt \{/source rt {/ms;
$sphinx_conf =~ s{\Q$RT::VarPath\E/sphinx/}{$tmp/}g;
$sphinx{'config'} = File::Spec->catfile( $tmp, 'sphinx.conf' );
plan skip_all => 'Not Oracle' unless RT->Config->Get('DatabaseType') eq 'Oracle';
plan tests => 13;
-RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1 );
+RT->Config->Set( FullTextSearch => Enable => 1, Indexed => 1, IndexName => 'rt_fts_index' );
setup_indexing();
use strict;
use warnings;
-use RT::Test tests => 181;
+use RT::Test tests => undef;
use Test::Warn;
use RT::Dashboard::Mailer;
my $mail = parse_mail( $mails[0] );
is($mail->head->get('Subject'), $subject);
is($mail->head->get('From'), "root\n");
+ is($mail->head->get('Content-Transfer-Encoding'), "base64\n");
is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n");
is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n");
- SKIP: {
- skip 'Weird MIME failure', 2;
- my $body = $mail->stringify_body;
- like($body, qr{My dashboards});
- like($body, qr{<a href="http://[^/]+/Dashboards/\d+/Testing!">Testing!</a>});
- };
+ my $body = $mail->bodyhandle->as_string;
+ like($body, qr{My dashboards});
+ like($body, qr{<a href="http://[^/]+/Dashboards/\d+/Testing!">Testing!</a>});
} # }}}
sub produces_no_dashboard_mail_ok { # {{{
is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n");
is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n");
-SKIP: {
- skip 'Weird MIME failure', 2;
- my $body = $mail->stringify_body;
- unlike($body, qr{My dashboards});
- unlike($body, qr{Testing!});
-};
+my $body = $mail->bodyhandle->as_string;
+unlike($body, qr{My dashboards});
+unlike($body, qr{Testing!});
delete_dashboard($dashboard_id);
Time => $bad_time,
);
+done_testing;
# the first time get the content
email_digest_like( '--mode daily --print', qr/in the last day/ );
# The second time run it for real so we make sure that we get RT to mark the txn as sent
-email_digest_like( '--mode daily', qr/maildaily\@/ );
+email_digest_like( '--mode daily --verbose', qr/maildaily\@/ );
# now we should have nothing to do, so no content.
email_digest_like( '--mode daily --print', '' );
# the first time get the content
email_digest_like( '--mode weekly --print', qr/in the last seven days/ );
# The second time run it for real so we make sure that we get RT to mark the txn as sent
-email_digest_like( '--mode weekly', qr/mailweekly\@/ );
+email_digest_like( '--mode weekly --verbose', qr/mailweekly\@/ );
# now we should have nothing to do, so no content.
email_digest_like( '--mode weekly --print', '' );
$mail =~ s/^Content-disposition: .+?\n(?=\n)//ism;
my $rt = send_and_receive($mail);
- like $rt, qr/Content-Disposition:\s*inline.+?filename\.patch/is, 'found default (inline) disposition';
+ like $rt, qr/Content-Disposition:\s*attachment.+?filename\.patch/is, 'found default (attachment) disposition';
}
sub send_and_receive {
use strict;
use warnings;
-use RT::Test::GnuPG tests => 216, gnupg_options => { passphrase => 'rt-test' };
+use RT::Test::GnuPG tests => 232, gnupg_options => { passphrase => 'rt-test' };
diag "load Everyone group";
my $everyone;
$m->content_like(qr/This is .*ID:$eid/ims, "$eid: content is there and message is decrypted");
$m->next_warning_like(qr/public key not found/);
+ $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
# some mails contain multiple signatures
if ($eid == 5 || $eid == 17 || $eid == 18) {
$m->next_warning_like(qr/public key not found/);
+ $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
}
$m->no_leftover_warnings_ok;
use strict;
use warnings;
-use RT::Test nodb => 1, tests => 13;
+use RT::Test nodb => 1, tests => 14;
use_ok('RT::I18N');
);
}
+diag q{adding quotes around mime words containing specials when word is already quoted};
+{
+ my $str = <<"END";
+Content-Disposition: attachment; filename="=?iso-8859-1?Q?foobar,_?=
+ =?iso-8859-1?Q?barfoo.docx?="
+END
+ my $decoded = 'Content-Disposition: attachment; filename="foobar, barfoo.docx"';
+ is( RT::I18N::DecodeMIMEWordsToUTF8($str), $decoded, "No added quotes" );
+}
+
diag q{regression test for #5248 from rt3.fsck.com};
{
my $str = qq{Subject: =?ISO-8859-1?Q?Re=3A_=5BXXXXXX=23269=5D_=5BComment=5D_Frag?=}
BEGIN {
### after: push @INC, qw(@RT_LIB_PATH@);
+
+ use RT;
+ RT->LoadConfig;
+ RT->InitPluginPaths;
+ RT->InitClasses;
}
-use RT::Shredder;
+
+require RT::Shredder;
=head1 DESCRIPTION
my $old_fhkn = $dbh->{'FetchHashKeyName'};
$dbh->{'FetchHashKeyName'} = 'NAME_lc';
- my $sth = $dbh->table_info( '', '%', '%', 'TABLE' ) || die $DBI::err;
- my @tables = keys %{$sth->fetchall_hashref( 'table_name' )};
+ my @tables = $RT::Handle->_TableNames( $dbh );
my $res = {};
foreach my $t( @tables ) {
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 98;
+use RT::Test nodata => 1, tests => 100;
use RT::Ticket;
my $q = RT::Test->load_or_create_queue( Name => 'Regression' );
}
run_tests();
+# make sure search by id is on LocalXXX columns
+{
+ my $tickets = RT::Tickets->new( RT->SystemUser );
+ $tickets->FromSQL('MemberOf = '. $tickets[0]->id);
+ like $tickets->BuildSelectQuery, qr/LocalBase/;
+ like $tickets->BuildSelectQuery, qr/LocalTarget/;
+}
+
# another set with tests of combinations searches
@tickets = RT::Test->create_tickets(
{ Queue => $q->id },
use strict;
use warnings;
-use RT::Test nodata => 1, tests => 2108;
+use RT::Test nodata => 1, tests => undef;
use RT::Ticket;
my $q = RT::Test->load_or_create_queue( Name => 'Regression' );
run_test( $query, %checks );
} }
}
-# XXX: It
-# @queries = (
-# '? AND ? AND ?' => sub { $_[0] and $_[1] and $_[2] },
-# '(? OR ?) AND ?' => sub { return (($_[0] or $_[1]) and $_[2]) },
-# '? OR (? AND ?)' => sub { $_[0] or ($_[1] and $_[2]) },
-# '(? AND ?) OR ?' => sub { ($_[0] and $_[1]) or $_[2] },
-# '? AND (? OR ?)' => sub { $_[0] and ($_[1] or $_[2]) },
-# '? OR ? OR ?' => sub { $_[0] or $_[1] or $_[2] },
-# );
-# while ( my ($template, $t_cb) = splice @queries, 0, 2 ) {
-# my @atmp = @conditions;
-# while ( my ($a, $a_cb) = splice @atmp, 0, 2 ) {
-# my @btmp = @conditions;
-# while ( my ($b, $b_cb) = splice @btmp, 0, 2 ) {
-# next if $a eq $b;
-# my @ctmp = @conditions;
-# while ( my ($c, $c_cb) = splice @ctmp, 0, 2 ) {
-# next if $a eq $c;
-# next if $b eq $c;
-#
-# my %checks = ();
-# foreach my $ticket ( @tickets ) {
-# my $s = $ticket->Subject;
-# $checks{ $s } = $t_cb->( scalar $a_cb->($s), scalar $b_cb->($s), scalar $c_cb->($s) );
-# }
-#
-# my $query = $template;
-# foreach my $tmp ($a, $b, $c) {
-# $query =~ s/\?/$tmp/;
-# }
-#
-# run_test( $query, %checks );
-# } } }
-# }
+ return unless $ENV{'RT_TEST_HEAVY'};
+
+ @queries = (
+ '? AND ? AND ?' => sub { $_[0] and $_[1] and $_[2] },
+ '(? OR ?) AND ?' => sub { return (($_[0] or $_[1]) and $_[2]) },
+ '? OR (? AND ?)' => sub { $_[0] or ($_[1] and $_[2]) },
+ '(? AND ?) OR ?' => sub { ($_[0] and $_[1]) or $_[2] },
+ '? AND (? OR ?)' => sub { $_[0] and ($_[1] or $_[2]) },
+ '? OR ? OR ?' => sub { $_[0] or $_[1] or $_[2] },
+ );
+ while ( my ($template, $t_cb) = splice @queries, 0, 2 ) {
+ my @atmp = @conditions;
+ while ( my ($a, $a_cb) = splice @atmp, 0, 2 ) {
+ my @btmp = @conditions;
+ while ( my ($b, $b_cb) = splice @btmp, 0, 2 ) {
+ next if $a eq $b;
+ my @ctmp = @conditions;
+ while ( my ($c, $c_cb) = splice @ctmp, 0, 2 ) {
+ next if $a eq $c;
+ next if $b eq $c;
+
+ my %checks = ();
+ foreach my $ticket ( @tickets ) {
+ my $s = $ticket->Subject;
+ $checks{ $s } = $t_cb->( scalar $a_cb->($s), scalar $b_cb->($s), scalar $c_cb->($s) );
+ }
+
+ my $query = $template;
+ foreach my $tmp ($a, $b, $c) {
+ $query =~ s/\?/$tmp/;
+ }
+
+ run_test( $query, %checks );
+ } } }
+ }
}
}
@tickets = ();
-
+done_testing();
# delete RT::Article's Name method on the server so we'll need to AUTOLOAD it
my $clone = $m->clone;
$clone->get_ok('/delete-article-name-method.html');
-like($clone->content, qr/{deleted}/);
+like($clone->content, qr/\{deleted\}/);
$m->form_name('TicketUpdate');
$m->click('SubmitTicket');
require JSON;
is_deeply(
JSON::from_json( $m->content ),
- [{"value" => "root\@localhost","label" => "Enoch Root"}]
+ [{"value" => "root\@localhost","label" => "Enoch Root", id=>$root_id}]
);
}
use strict;
use warnings;
-use RT::Test tests => 35;
+use RT::Test tests => undef;
my ( $baseurl, $m ) = RT::Test->started_ok;
ok $m->login, 'logged in as root';
$m->content_contains('test cf date:', 'has no cf date field on the page' );
$m->content_lacks('foodate', 'invalid dates not set' );
+
+ my @warnings = $m->get_warnings;
+ chomp @warnings;
+ is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} );
}
+
+done_testing;
use strict;
use warnings;
-use RT::Test tests => 51;
+use RT::Test tests => undef;
RT->Config->Set( 'Timezone' => 'EST5EDT' ); # -04:00
my ($baseurl, $m) = RT::Test->started_ok;
$m->content_contains('test cf datetime:', 'has cf datetime field on the page');
$m->content_lacks('foodate', 'invalid dates not set');
+
+ my @warnings = $m->get_warnings;
+ chomp @warnings;
+ is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} );
}
sub is_results_number {
# to make $m->DESTROY happy
undef $m;
+done_testing;
$m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Requestor.Phone" );
is( $m->content_type, "image/png" );
ok( length($m->content), "Has content" );
+
+diag "Confirm subnav links use Query param before saved search in session.";
+
+$m->get_ok( "/Search/Chart.html?Query=id>0" );
+my $advanced = $m->find_link( text => 'Advanced' )->URI->equery;
+like( $advanced, qr{Query=id%3E0},
+ 'Advanced link has Query param with id search'
+ );
+
+# Load the session with another search.
+$m->get_ok( "/Search/Results.html?Query=Queue='General'" );
+
+$m->get_ok( "/Search/Chart.html?Query=id>0" );
+$advanced = $m->find_link( text => 'Advanced' )->URI->equery;
+like( $advanced, qr{Query=id%3E0},
+ 'Advanced link still has Query param with id search'
+ );
# Test reading and setting custom fields without spaces
expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking initial value');
-expect_like(qr/CF\.{myCF$$}:/i, 'Verified initial empty value (CF-x syntax)');
+expect_like(qr/\QCF.{myCF$$}\E:/i, 'Verified initial empty value (CF-x syntax)');
expect_send("show ticket/$ticket_id -f CF.{myCF$$}", 'Checking initial value');
-expect_like(qr/CF\.{myCF$$}:/i, 'Verified initial empty value (CF.{x} syntax)');
+expect_like(qr/\QCF.{myCF$$}\E:/i, 'Verified initial empty value (CF.{x} syntax)');
expect_send("edit ticket/$ticket_id set 'CF-myCF$$=VALUE' ", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
-expect_like(qr/CF\.{myCF$$}: VALUE/i, 'Verified change');
+expect_like(qr/\QCF.{myCF$$}\E: VALUE/i, 'Verified change');
# Test setting 0 as value of the custom field
expect_send("edit ticket/$ticket_id set 'CF-myCF$$=0' ", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
-expect_like(qr/CF\.{myCF$$}: 0/i, 'Verified change');
+expect_like(qr/\QCF.{myCF$$}\E: 0/i, 'Verified change');
expect_send("edit ticket/$ticket_id set 'CF.{myCF$$}=VALUE' ",'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
expect_send("show ticket/$ticket_id -f CF.{myCF$$}", 'Checking new value');
-expect_like(qr/CF\.{myCF$$}: VALUE/i, 'Verified change');
+expect_like(qr/\QCF.{myCF$$}\E: VALUE/i, 'Verified change');
# Test setting 0 as value of the custom field
expect_send("edit ticket/$ticket_id set 'CF.{myCF$$}=0' ", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
expect_send("show ticket/$ticket_id -f CF.{myCF$$}", 'Checking new value');
-expect_like(qr/CF\.{myCF$$}: 0/i, 'Verified change');
+expect_like(qr/\QCF.{myCF$$}\E: 0/i, 'Verified change');
# Test reading and setting custom fields with spaces
expect_send("show ticket/$ticket_id -f 'CF-my CF$$'", 'Checking initial value');
-expect_like(qr/CF\.{my CF$$}:/i, 'Verified change');
+expect_like(qr/\QCF.{my CF$$}\E:/i, 'Verified change');
expect_send("edit ticket/$ticket_id set 'CF-my CF$$=VALUE' ", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
expect_send("show ticket/$ticket_id -f 'CF-my CF$$'", 'Checking new value');
-expect_like(qr/CF\.{my CF$$}: VALUE/i, 'Verified change');
+expect_like(qr/\QCF.{my CF$$}\E: VALUE/i, 'Verified change');
expect_send("ls -l 'id = $ticket_id' -f 'CF-my CF$$'", 'Checking new value');
-expect_like(qr/CF\.{my CF$$}: VALUE/i, 'Verified change');
+expect_like(qr/\QCF.{my CF$$}\E: VALUE/i, 'Verified change');
expect_send("show ticket/$ticket_id -f 'CF.{my CF$$}'", 'Checking initial value');
-expect_like(qr/CF\.{my CF$$}: VALUE/i, 'Verified change');
+expect_like(qr/\QCF.{my CF$$}\E: VALUE/i, 'Verified change');
expect_send("edit ticket/$ticket_id set 'CF.{my CF$$}=NEW' ", 'Changing CF...');
expect_send("show ticket/$ticket_id -f 'CF.{my CF$$}'", 'Checking new value');
-expect_like(qr/CF\.{my CF$$}: NEW/i, 'Verified change');
+expect_like(qr/\QCF.{my CF$$}\E: NEW/i, 'Verified change');
expect_send("ls -l 'id = $ticket_id' -f 'CF.{my CF$$}'", 'Checking new value');
-expect_like(qr/CF\.{my CF$$}: NEW/i, 'Verified change');
+expect_like(qr/\QCF.{my CF$$}\E: NEW/i, 'Verified change');
# Test reading and setting single value custom field with commas or quotes
expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking initial value');
-expect_like(qr/CF\.{myCF$$}:/i, 'Verified change');
+expect_like(qr/\QCF.{myCF$$}\E:/i, 'Verified change');
expect_send("edit ticket/$ticket_id set CF-myCF$$=1,2,3", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
-expect_like(qr/CF\.{myCF$$}: 1,2,3/i, 'Verified change');
+expect_like(qr/\QCF.{myCF$$}\E: 1,2,3/i, 'Verified change');
expect_send("edit ticket/$ticket_id set CF-myCF$$=\"1's,2,3\"", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
-expect_like(qr/CF\.{myCF$$}: 1's,2,3/i, 'Verified change');
+expect_like(qr/\QCF.{myCF$$}\E: 1's,2,3/i, 'Verified change');
# Test reading and setting custom fields with multiple values
expect_send("show ticket/$ticket_id -f CF-MultipleCF$$", 'Checking initial value');
-expect_like(qr/CF\.{MultipleCF$$}:/i, 'Verified multiple cf change');
+expect_like(qr/\QCF.{MultipleCF$$}\E:/i, 'Verified multiple cf change');
expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=1,2,3 ", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: 1,\s*2,\s*3/i, 'Verified multiple cf change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: 1,\s*2,\s*3/i, 'Verified multiple cf change');
expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=a,b,c ", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: a,\s*b,\s*c/i, 'Verified change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: a,\s*b,\s*c/i, 'Verified change');
expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'del multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: b,\s*c/i, 'Verified multiple cf change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: b,\s*c/i, 'Verified multiple cf change');
expect_send("edit ticket/$ticket_id add CF.{MultipleCF$$}=o", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: b,\s*c,\s*o/i, 'Verified multiple cf change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: b,\s*c,\s*o/i, 'Verified multiple cf change');
expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"'a,b,c'\" ", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: 'a,b,c'/i, 'Verified change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change');
expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: 'a,b,c'/i, 'Verified change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change');
expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=q{a,b,c}", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: 'a,b,c'/i, 'Verified change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change');
expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: 'a,b,c'/i, 'Verified change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change');
expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=\"'a,b,c'\"", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: \s*$/i, 'Verified change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: \s*$/i, 'Verified change');
expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"q{1,2's,3}\"", 'Changing CF...');
expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
-expect_like(qr/CF\.{MultipleCF$$}: '1,2\\'s,3'/i, 'Verified change');
+expect_like(qr/\QCF.{MultipleCF$$}\E: '1,2\\'s,3'/i, 'Verified change');
# ...
# change a ticket's ...[other properties]...
use warnings;
use RT::Test::GnuPG
- tests => 102,
+ tests => 104,
gnupg_options => {
passphrase => 'recipient',
'trust-model' => 'always',
like($content, qr/KR-nokey\@example.com \(no pubkey!\)-K/, "KeyRequestors DOES issue no-pubkey warning for nokey\@example.com");
$m->next_warning_like(qr/public key not found/);
+$m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
$m->next_warning_like(qr/public key not found/);
+$m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
$m->no_leftover_warnings_ok;
use strict;
use warnings;
-use RT::Test::GnuPG tests => 79, gnupg_options => { passphrase => 'rt-test' };
+use RT::Test::GnuPG tests => 83, gnupg_options => { passphrase => 'rt-test' };
use RT::Action::SendEmail;
my $queue = RT::Test->load_or_create_queue(
my @mail = RT::Test->fetch_caught_mails;
ok !@mail, 'there are no outgoing emails';
- $m->next_warning_like(qr/public key not found/) for 1 .. 4;
+ for (1 .. 4) {
+ $m->next_warning_like(qr/public key not found/) ;
+ $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
+ }
+
$m->no_leftover_warnings_ok;
}
use strict;
use warnings;
-use RT::Test::GnuPG tests => 86, gnupg_options => { passphrase => 'rt-test' };
+use RT::Test::GnuPG tests => 88, gnupg_options => { passphrase => 'rt-test' };
use RT::Action::SendEmail;
my @mail = RT::Test->fetch_caught_mails;
ok !@mail, 'there are no outgoing emails';
- $m->next_warning_like(qr/public key not found/) for 1 .. 2;
+ for (1 .. 2) {
+ $m->next_warning_like(qr/public key not found/);
+ $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/);
+ }
$m->no_leftover_warnings_ok;
}
$m->submit_form(with_fields => {
DatabaseAdmin => $ENV{RT_DBA_USER},
DatabaseAdminPassword => $ENV{RT_DBA_PASSWORD},
+ DatabasePassword => "rt_pass",
});
}
$m->content_contains('Connection succeeded');
my $reminder = $col->First;
is($reminder->Subject, "baby's first reminder");
my $reminder_id = $reminder->id;
-is($reminder->Status, 'new');
+is($reminder->Status, 'open');
-$ticket->SetStatus('new');
-is( $ticket->Status, 'new', 'changed back to new' );
+$ticket->SetStatus('open');
+is( $ticket->Status, 'open', 'changed back to new' );
$m->goto_ticket($ticket->id);
$m->text_contains('New reminder:', "can create a new reminder");
$reminder = RT::Ticket->new($user);
$reminder->Load($reminder_id);
is($reminder->Subject, 'changed the subject');
-is($reminder->Status, 'new');
+is($reminder->Status, 'open');
$m->goto_ticket($ticket->id);
$m->form_name('UpdateReminders');
]
);
$text = $m->content;
- like( $text, qr/^CF\.{test}: baz\s*$/m, 'cf value in rest show' );
+ like( $text, qr/^CF\.\{test\}: baz\s*$/m, 'cf value in rest show' );
$text =~ s{.*}{}; # remove header
- $text =~ s!CF\.{test}: baz!CF.{test}: newbaz!;
+ $text =~ s!CF\.\{test\}: baz!CF.{test}: newbaz!;
$m->post(
"$baseurl/REST/1.0/ticket/edit",
[
use strict;
use warnings;
-use RT::Test tests => 9;
+
+use RT::Test
+ tests => 17,
+ config => 'Set( $ShowUnreadMessageNotifications, 1 );'
+;
my ($url, $m) = RT::Test->started_ok;
-my ($ticket) =
- RT::Test->create_ticket( Queue => 'General', Subject => 'test subject' );
+my $user_a = RT::Test->load_or_create_user(
+ Name => 'user_a',
+ Password => 'password',
+ EmailAddress => 'user_a@example.com',
+ Privileged => 0,
+);
+ok( $user_a && $user_a->id, 'loaded or created user' );
+ok( ! $user_a->Privileged, 'user is not privileged' );
+
+# Load Cc group
+my $Cc = RT::Group->new( RT->SystemUser );
+my($ok, $msg) = $Cc->LoadSystemRoleGroup( 'Cc' );
+ok($ok, $msg);
+RT::Test->add_rights( { Principal => $Cc, Right => ['ShowTicket'] } );
+
+my ($ticket) = RT::Test->create_ticket(
+ Queue => 'General',
+ Subject => 'test subject',
+ Cc => 'user_a@example.com',
+);
+
+my @results = $ticket->Correspond( Content => 'sample correspondence' );
-$m->login();
+ok( $m->login('user_a' => 'password'), 'unprivileged user logged in' );
$m->get_ok( '/SelfService/Display.html?id=' . $ticket->id,
'got selfservice display page' );
$m->title_is( $title );
$m->content_contains( "<h1>$title</h1>", "contains <h1>$title</h1>" );
+# $ShowUnreadMessageNotifications tests:
+$m->content_contains( "There are unread messages on this ticket." );
+
+# mark the message as read
+$m->follow_link_ok(
+ { text => 'jump to the first unread message and mark all messages as seen' },
+ 'followed mark as seen link'
+);
+
+$m->content_contains( "<h1>$title</h1>", "contains <h1>$title</h1>" );
+$m->content_lacks( "There are unread messages on this ticket." );
+
# TODO need more SelfService tests
$m->text_contains("実名", "Page content is japanese");
$m->submit_form_ok({ with_fields => { Lang => ''} },
"And set to the default");
-$m->text_contains("Langは「'ja'」から「''」に変更されました");
+$m->text_contains("Langは「'ja'」から「(値なし)」に変更されました");
$m->text_contains("Real Name", "Page content is english");
undef $m;