diff options
Diffstat (limited to 'rt/lib/RT/Interface')
-rw-r--r-- | rt/lib/RT/Interface/CLI.pm | 246 | ||||
-rwxr-xr-x | rt/lib/RT/Interface/Email.pm | 648 | ||||
-rwxr-xr-x | rt/lib/RT/Interface/Email/Auth/GnuPG.pm | 121 | ||||
-rw-r--r-- | rt/lib/RT/Interface/Email/Auth/MailFrom.pm | 182 | ||||
-rw-r--r-- | rt/lib/RT/Interface/Email/Filter/SpamAssassin.pm | 94 | ||||
-rw-r--r-- | rt/lib/RT/Interface/REST.pm | 274 | ||||
-rw-r--r-- | rt/lib/RT/Interface/Web.pm | 1377 | ||||
-rw-r--r-- | rt/lib/RT/Interface/Web/Handler.pm | 221 | ||||
-rwxr-xr-x | rt/lib/RT/Interface/Web/QueryBuilder.pm | 56 | ||||
-rwxr-xr-x | rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm | 245 | ||||
-rwxr-xr-x | rt/lib/RT/Interface/Web/Standalone.pm | 37 | ||||
-rw-r--r-- | rt/lib/RT/Interface/Web_Vendor.pm | 95 |
12 files changed, 0 insertions, 3596 deletions
diff --git a/rt/lib/RT/Interface/CLI.pm b/rt/lib/RT/Interface/CLI.pm deleted file mode 100644 index ec0e877b4..000000000 --- a/rt/lib/RT/Interface/CLI.pm +++ /dev/null @@ -1,246 +0,0 @@ -# BEGIN LICENSE BLOCK -# -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> -# -# (Except where explictly superceded by other copyright notices) -# -# 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. -# -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. -# -# -# END LICENSE BLOCK -use strict; - -use RT; -package RT::Interface::CLI; - - - -BEGIN { - use Exporter (); - use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); - - # set the version for version checking - $VERSION = do { my @r = (q$Revision: 1.2 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker - - @ISA = qw(Exporter); - - # your exported package globals go here, - # as well as any optionally exported functions - @EXPORT_OK = qw(&CleanEnv - &GetCurrentUser &GetMessageContent &debug &loc); -} - -=head1 NAME - - RT::Interface::CLI - helper functions for creating a commandline RT interface - -=head1 SYNOPSIS - - use lib "/path/to/rt/libraries/"; - - use RT::Interface::CLI qw(CleanEnv - GetCurrentUser GetMessageContent loc); - - #Clean out all the nasties from the environment - CleanEnv(); - - #let's talk to RT' - use RT; - - #Load RT's config file - RT::LoadConfig(); - - # Connect to the database. set up loggign - RT::Init(); - - #Get the current user all loaded - my $CurrentUser = GetCurrentUser(); - - print loc('Hello!'); # Synonym of $CuurentUser->loc('Hello!'); - -=head1 DESCRIPTION - - -=head1 METHODS - -=begin testing - -ok(require RT::Interface::CLI); - -=end testing - -=cut - - -=head2 CleanEnv - -Removes some of the nastiest nasties from the user\'s environment. - -=cut - -sub CleanEnv { - $ENV{'PATH'} = '/bin:/usr/bin'; # or whatever you need - $ENV{'CDPATH'} = '' if defined $ENV{'CDPATH'}; - $ENV{'SHELL'} = '/bin/sh' if defined $ENV{'SHELL'}; - $ENV{'ENV'} = '' if defined $ENV{'ENV'}; - $ENV{'IFS'} = '' if defined $ENV{'IFS'}; -} - - - - -{ - - my $CurrentUser; # shared betwen GetCurrentUser and loc - -# {{{ sub GetCurrentUser - -=head2 GetCurrentUser - - Figures out the uid of the current user and returns an RT::CurrentUser object -loaded with that user. if the current user isn't found, returns a copy of RT::Nobody. - -=cut - -sub GetCurrentUser { - - require RT::CurrentUser; - - #Instantiate a user object - - my $Gecos= ($^O eq 'MSWin32') ? Win32::LoginName() : (getpwuid($<))[0]; - - #If the current user is 0, then RT will assume that the User object - #is that of the currentuser. - - $CurrentUser = new RT::CurrentUser(); - $CurrentUser->LoadByGecos($Gecos); - - unless ($CurrentUser->Id) { - $RT::Logger->debug("No user with a unix login of '$Gecos' was found. "); - } - - return($CurrentUser); -} -# }}} - - -# {{{ sub loc - -=head2 loc - - Synonym of $CurrentUser->loc(). - -=cut - -sub loc { - die "No current user yet" unless $CurrentUser ||= RT::CurrentUser->new; - return $CurrentUser->loc(@_); -} -# }}} - -} - - -# {{{ sub GetMessageContent - -=head2 GetMessageContent - -Takes two arguments a source file and a boolean "edit". If the source file -is undef or "", assumes an empty file. Returns an edited file as an -array of lines. - -=cut - -sub GetMessageContent { - my %args = ( Source => undef, - Content => undef, - Edit => undef, - CurrentUser => undef, - @_); - my $source = $args{'Source'}; - - my $edit = $args{'Edit'}; - - my $currentuser = $args{'CurrentUser'}; - my @lines; - - use File::Temp qw/ tempfile/; - - #Load the sourcefile, if it's been handed to us - if ($source) { - open (SOURCE, "<$source"); - @lines = (<SOURCE>); - close (SOURCE); - } - elsif ($args{'Content'}) { - @lines = split('\n',$args{'Content'}); - } - #get us a tempfile. - my ($fh, $filename) = tempfile(); - - #write to a tmpfile - for (@lines) { - print $fh $_; - } - close ($fh); - - #Edit the file if we need to - if ($edit) { - - unless ($ENV{'EDITOR'}) { - $RT::Logger->crit('No $EDITOR variable defined'. "\n"); - return undef; - } - system ($ENV{'EDITOR'}, $filename); - } - - open (READ, "<$filename"); - my @newlines = (<READ>); - close (READ); - - unlink ($filename) unless (debug()); - return(\@newlines); - -} - -# }}} - -# {{{ sub debug - -sub debug { - my $val = shift; - my ($debug); - if ($val) { - $RT::Logger->debug($val."\n"); - if ($debug) { - print STDERR "$val\n"; - } - } - if ($debug) { - return(1); - } -} - -# }}} - - -eval "require RT::Interface::CLI_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/CLI_Vendor.pm}); -eval "require RT::Interface::CLI_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/CLI_Local.pm}); - -1; diff --git a/rt/lib/RT/Interface/Email.pm b/rt/lib/RT/Interface/Email.pm deleted file mode 100755 index 7eec0502f..000000000 --- a/rt/lib/RT/Interface/Email.pm +++ /dev/null @@ -1,648 +0,0 @@ -# BEGIN LICENSE BLOCK -# -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> -# -# (Except where explictly superceded by other copyright notices) -# -# 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. -# -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. -# -# -# END LICENSE BLOCK -package RT::Interface::Email; - -use strict; -use Mail::Address; -use MIME::Entity; -use RT::EmailParser; - - -BEGIN { - use Exporter (); - use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); - - # set the version for version checking - $VERSION = do { my @r = (q$Revision: 1.2 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker - - @ISA = qw(Exporter); - - # your exported package globals go here, - # as well as any optionally exported functions - @EXPORT_OK = qw( - &CreateUser - &GetMessageContent - &CheckForLoops - &CheckForSuspiciousSender - &CheckForAutoGenerated - &MailError - &ParseCcAddressesFromHead - &ParseSenderAddressFromHead - &ParseErrorsToAddressFromHead - &ParseAddressFromHeader - &Gateway); - -} - -=head1 NAME - - RT::Interface::CLI - helper functions for creating a commandline RT interface - -=head1 SYNOPSIS - - use lib "!!RT_LIB_PATH!!"; - use lib "!!RT_ETC_PATH!!"; - - use RT::Interface::Email qw(Gateway CreateUser); - -=head1 DESCRIPTION - - -=begin testing - -ok(require RT::Interface::Email); - -=end testing - - -=head1 METHODS - -=cut - - -# {{{ sub CheckForLoops - -sub CheckForLoops { - my $head = shift; - - #If this instance of RT sent it our, we don't want to take it in - my $RTLoop = $head->get("X-RT-Loop-Prevention") || ""; - chomp ($RTLoop); #remove that newline - if ($RTLoop eq "$RT::rtname") { - return (1); - } - - # TODO: We might not trap the case where RT instance A sends a mail - # to RT instance B which sends a mail to ... - return (undef); -} - -# }}} - -# {{{ sub CheckForSuspiciousSender - -sub CheckForSuspiciousSender { - my $head = shift; - - #if it's from a postmaster or mailer daemon, it's likely a bounce. - - #TODO: better algorithms needed here - there is no standards for - #bounces, so it's very difficult to separate them from anything - #else. At the other hand, the Return-To address is only ment to be - #used as an error channel, we might want to put up a separate - #Return-To address which is treated differently. - - #TODO: search through the whole email and find the right Ticket ID. - - my ($From, $junk) = ParseSenderAddressFromHead($head); - - if (($From =~ /^mailer-daemon/i) or - ($From =~ /^postmaster/i)){ - return (1); - - } - - return (undef); - -} - -# }}} - -# {{{ sub CheckForAutoGenerated -sub CheckForAutoGenerated { - my $head = shift; - - my $Precedence = $head->get("Precedence") || "" ; - if ($Precedence =~ /^(bulk|junk)/i) { - return (1); - } - else { - return (0); - } -} - -# }}} - - -# {{{ sub MailError -sub MailError { - my %args = (To => $RT::OwnerEmail, - Bcc => undef, - From => $RT::CorrespondAddress, - Subject => 'There has been an error', - Explanation => 'Unexplained error', - MIMEObj => undef, - LogLevel => 'crit', - @_); - - - $RT::Logger->log(level => $args{'LogLevel'}, - message => $args{'Explanation'} - ); - my $entity = MIME::Entity->build( Type =>"multipart/mixed", - From => $args{'From'}, - Bcc => $args{'Bcc'}, - To => $args{'To'}, - Subject => $args{'Subject'}, - 'X-RT-Loop-Prevention' => $RT::rtname, - ); - - $entity->attach( Data => $args{'Explanation'}."\n"); - - my $mimeobj = $args{'MIMEObj'}; - if ($mimeobj) { - $mimeobj->sync_headers(); - $entity->add_part($mimeobj); - } - - if ($RT::MailCommand eq 'sendmailpipe') { - open (MAIL, "|$RT::SendmailPath $RT::SendmailArguments") || return(0); - print MAIL $entity->as_string; - close(MAIL); - } - else { - $entity->send($RT::MailCommand, $RT::MailParams); - } -} - -# }}} - -# {{{ Create User - -sub CreateUser { - my ($Username, $Address, $Name, $ErrorsTo, $entity) = @_; - my $NewUser = RT::User->new($RT::SystemUser); - - # This data is tainted by some Very Broken mailers. - # (Sometimes they send raw ISO 8859-1 data here. fear that. - require Encode; - $Username = Encode::encode(utf8 => $Username, Encode::FB_PERLQQ()) if defined $Username; - $Name = Encode::encode(utf8 => $Name, Encode::FB_PERLQQ()) if defined $Name; - - my ($Val, $Message) = - $NewUser->Create(Name => ($Username || $Address), - EmailAddress => $Address, - RealName => $Name, - Password => undef, - Privileged => 0, - Comments => 'Autocreated on ticket submission' - ); - - unless ($Val) { - - # Deal with the race condition of two account creations at once - # - if ($Username) { - $NewUser->LoadByName($Username); - } - - unless ($NewUser->Id) { - $NewUser->LoadByEmail($Address); - } - - unless ($NewUser->Id) { - MailError( To => $ErrorsTo, - Subject => "User could not be created", - Explanation => "User creation failed in mailgateway: $Message", - MIMEObj => $entity, - LogLevel => 'crit' - ); - } - } - - #Load the new user object - my $CurrentUser = RT::CurrentUser->new(); - $CurrentUser->LoadByEmail($Address); - - unless ($CurrentUser->id) { - $RT::Logger->warning("Couldn't load user '$Address'.". "giving up"); - MailError( To => $ErrorsTo, - Subject => "User could not be loaded", - Explanation => "User '$Address' could not be loaded in the mail gateway", - MIMEObj => $entity, - LogLevel => 'crit' - ); - } - - return $CurrentUser; -} -# }}} -# {{{ ParseCcAddressesFromHead - -=head2 ParseCcAddressesFromHead HASHREF - -Takes a hashref object containing QueueObj, Head and CurrentUser objects. -Returns a list of all email addresses in the To and Cc -headers b<except> the current Queue\'s email addresses, the CurrentUser\'s -email address and anything that the configuration sub RT::IsRTAddress matches. - -=cut - -sub ParseCcAddressesFromHead { - my %args = ( Head => undef, - QueueObj => undef, - CurrentUser => undef, - @_ ); - - my (@Addresses); - - my @ToObjs = Mail::Address->parse($args{'Head'}->get('To')); - my @CcObjs = Mail::Address->parse($args{'Head'}->get('Cc')); - - foreach my $AddrObj (@ToObjs, @CcObjs) { - my $Address = $AddrObj->address; - $Address = $args{'CurrentUser'}->UserObj->CanonicalizeEmailAddress($Address); - next if ($args{'CurrentUser'}->EmailAddress =~ /^$Address$/i); - next if ($args{'QueueObj'}->CorrespondAddress =~ /^$Address$/i); - next if ($args{'QueueObj'}->CommentAddress =~ /^$Address$/i); - next if (RT::EmailParser::IsRTAddress(undef, $Address)); - - push (@Addresses, $Address); - } - return (@Addresses); -} - - -# }}} - -# {{{ ParseSenderAdddressFromHead - -=head2 ParseSenderAddressFromHead - -Takes a MIME::Header object. Returns a tuple: (user@host, friendly name) -of the From (evaluated in order of Reply-To:, From:, Sender) - -=cut - -sub ParseSenderAddressFromHead { - my $head = shift; - #Figure out who's sending this message. - my $From = $head->get('Reply-To') || - $head->get('From') || - $head->get('Sender'); - return (ParseAddressFromHeader($From)); -} -# }}} - -# {{{ ParseErrorsToAdddressFromHead - -=head2 ParseErrorsToAddressFromHead - -Takes a MIME::Header object. Return a single value : user@host -of the From (evaluated in order of Errors-To:,Reply-To:, From:, Sender) - -=cut - -sub ParseErrorsToAddressFromHead { - my $head = shift; - #Figure out who's sending this message. - - foreach my $header ('Errors-To' , 'Reply-To', 'From', 'Sender' ) { - # If there's a header of that name - my $headerobj = $head->get($header); - if ($headerobj) { - my ($addr, $name ) = ParseAddressFromHeader($headerobj); - # If it's got actual useful content... - return ($addr) if ($addr); - } - } -} -# }}} - -# {{{ ParseAddressFromHeader - -=head2 ParseAddressFromHeader ADDRESS - -Takes an address from $head->get('Line') and returns a tuple: user@host, friendly name - -=cut - - -sub ParseAddressFromHeader{ - my $Addr = shift; - - my @Addresses = Mail::Address->parse($Addr); - - my $AddrObj = $Addresses[0]; - - unless (ref($AddrObj)) { - return(undef,undef); - } - - my $Name = ($AddrObj->phrase || $AddrObj->comment || $AddrObj->address); - - #Lets take the from and load a user object. - my $Address = $AddrObj->address; - - return ($Address, $Name); -} -# }}} - - - -=head2 Gateway - -This performs all the "guts" of the mail rt-mailgate program, and is -designed to be called from the web interface with a message, user -object, and so on. - -=cut - -sub Gateway { - my %args = ( message => undef, - queue => 1, - action => 'correspond', - ticket => undef, - @_ ); - - # Validate the action - unless ( $args{'action'} =~ /^(comment|correspond|action)$/ ) { - - # Can't safely loc this. What object do we loc around? - return ( 0, "Invalid 'action' parameter", undef ); - } - - my $parser = RT::EmailParser->new(); - $parser->ParseMIMEEntityFromScalar( $args{'message'} ); - - my $Message = $parser->Entity(); - my $head = $Message->head; - - my ( $CurrentUser, $AuthStat, $status, $error ); - - my $ErrorsTo = ParseErrorsToAddressFromHead($head); - - my $MessageId = $head->get('Message-Id') - || "<no-message-id-" . time . rand(2000) . "\@.$RT::Organization>"; - - #Pull apart the subject line - my $Subject = $head->get('Subject') || ''; - chomp $Subject; - - - $args{'ticket'} ||= $parser->ParseTicketId($Subject); - - my $SystemTicket; - if ($args{'ticket'} ) { - $SystemTicket = RT::Ticket->new($RT::SystemUser); - $SystemTicket->Load($args{'ticket'}); - } - - #Set up a queue object - my $SystemQueueObj = RT::Queue->new($RT::SystemUser); - $SystemQueueObj->Load( $args{'queue'} ); - - - # We can safely have no queue of we have a known-good ticket - unless ( $args{'ticket'} || $SystemQueueObj->id ) { - MailError( - To => $RT::OwnerEmail, - Subject => "RT Bounce: $Subject", - Explanation => "RT couldn't find the queue: " . $args{'queue'}, - MIMEObj => $Message ); - return ( 0, "RT couldn't find the queue: " . $args{'queue'}, undef ); - } - - # Authentication Level - # -1 - Get out. this user has been explicitly declined - # 0 - User may not do anything (Not used at the moment) - # 1 - Normal user - # 2 - User is allowed to specify status updates etc. a la enhanced-mailgate - - push @RT::MailPlugins, "Auth::MailFrom" unless @RT::MailPlugins; - # Since this needs loading, no matter what - - for (@RT::MailPlugins) { - my $Code; - my $NewAuthStat; - if ( ref($_) eq "CODE" ) { - $Code = $_; - } - else { - $_ = "RT::Interface::Email::$_" unless /^RT::Interface::Email::/; - eval "require $_;"; - if ($@) { - die ("Couldn't load module $_: $@"); - next; - } - no strict 'refs'; - if ( !defined( $Code = *{ $_ . "::GetCurrentUser" }{CODE} ) ) { - die ("No GetCurrentUser code found in $_ module"); - next; - } - } - - ( $CurrentUser, $NewAuthStat ) = $Code->( Message => $Message, - CurrentUser => $CurrentUser, - AuthLevel => $AuthStat, - Action => $args{'action'}, - Ticket => $SystemTicket, - Queue => $SystemQueueObj ); - - # You get the highest level of authentication you were assigned. - last if $AuthStat == -1; - $AuthStat = $NewAuthStat if $NewAuthStat > $AuthStat; - } - - # {{{ If authentication fails and no new user was created, get out. - if ( !$CurrentUser or !$CurrentUser->Id or $AuthStat == -1 ) { - - # If the plugins refused to create one, they lose. - MailError( - Subject => "Could not load a valid user", - Explanation => <<EOT, -RT could not load a valid user, and RT's configuration does not allow -for the creation of a new user for your email. - -Your RT administrator needs to grant 'Everyone' the right 'CreateTicket' -for this queue. - -EOT - MIMEObj => $Message, - LogLevel => 'error' ) - unless $AuthStat == -1; - return ( 0, "Could not load a valid user", undef ); - } - - # }}} - - # {{{ Lets check for mail loops of various sorts. - my $IsAutoGenerated = CheckForAutoGenerated($head); - - my $IsSuspiciousSender = CheckForSuspiciousSender($head); - - my $IsALoop = CheckForLoops($head); - - my $SquelchReplies = 0; - - #If the message is autogenerated, we need to know, so we can not - # send mail to the sender - if ( $IsSuspiciousSender || $IsAutoGenerated || $IsALoop ) { - $SquelchReplies = 1; - $ErrorsTo = $RT::OwnerEmail; - } - - # }}} - - # {{{ Drop it if it's disallowed - if ( $AuthStat == 0 ) { - MailError( - To => $ErrorsTo, - Subject => "Permission Denied", - Explanation => "You do not have permission to communicate with RT", - MIMEObj => $Message ); - } - - # }}} - # {{{ Warn someone if it's a loop - - # Warn someone if it's a loop, before we drop it on the ground - if ($IsALoop) { - $RT::Logger->crit("RT Recieved mail ($MessageId) from itself."); - - #Should we mail it to RTOwner? - if ($RT::LoopsToRTOwner) { - MailError( To => $RT::OwnerEmail, - Subject => "RT Bounce: $Subject", - Explanation => "RT thinks this message may be a bounce", - MIMEObj => $Message ); - - #Do we actually want to store it? - return ( 0, "Message Bounced", undef ) unless ($RT::StoreLoops); - } - } - - # }}} - - # {{{ Squelch replies if necessary - # Don't let the user stuff the RT-Squelch-Replies-To header. - if ( $head->get('RT-Squelch-Replies-To') ) { - $head->add( 'RT-Relocated-Squelch-Replies-To', - $head->get('RT-Squelch-Replies-To') ); - $head->delete('RT-Squelch-Replies-To'); - } - - if ($SquelchReplies) { - ## TODO: This is a hack. It should be some other way to - ## indicate that the transaction should be "silent". - - my ( $Sender, $junk ) = ParseSenderAddressFromHead($head); - $head->add( 'RT-Squelch-Replies-To', $Sender ); - } - - # }}} - - my $Ticket = RT::Ticket->new($CurrentUser); - - # {{{ If we don't have a ticket Id, we're creating a new ticket - if ( !$args{'ticket'} ) { - - # {{{ Create a new ticket - - my @Cc; - my @Requestors = ( $CurrentUser->id ); - - if ($RT::ParseNewMessageForTicketCcs) { - @Cc = ParseCcAddressesFromHead( Head => $head, - CurrentUser => $CurrentUser, - QueueObj => $SystemQueueObj ); - } - - my ( $id, $Transaction, $ErrStr ) = $Ticket->Create( - Queue => $SystemQueueObj->Id, - Subject => $Subject, - Requestor => \@Requestors, - Cc => \@Cc, - MIMEObj => $Message ); - if ( $id == 0 ) { - MailError( To => $ErrorsTo, - Subject => "Ticket creation failed", - Explanation => $ErrStr, - MIMEObj => $Message ); - $RT::Logger->error("Create failed: $id / $Transaction / $ErrStr "); - return ( 0, "Ticket creation failed", $Ticket ); - } - - # }}} - } - - # }}} - - # If the action is comment, add a comment. - elsif ( $args{'action'} =~ /^(comment|correspond)$/i ) { - $Ticket->Load($args{'ticket'}); - unless ( $Ticket->Id ) { - my $message = "Could not find a ticket with id ".$args{'ticket'}; - MailError( To => $ErrorsTo, - Subject => "Message not recorded", - Explanation => $message, - MIMEObj => $Message ); - - return ( 0, $message); - } - - my ( $status, $msg ); - if ( $args{'action'} =~ /^correspond$/ ) { - ( $status, $msg ) = $Ticket->Correspond( MIMEObj => $Message ); - } - else { - ( $status, $msg ) = $Ticket->Comment( MIMEObj => $Message ); - } - unless ($status) { - - #Warn the sender that we couldn't actually submit the comment. - MailError( To => $ErrorsTo, - Subject => "Message not recorded", - Explanation => $msg, - MIMEObj => $Message ); - return ( 0, "Message not recorded", $Ticket ); - } - } - - else { - - #Return mail to the sender with an error - MailError( To => $ErrorsTo, - Subject => "RT Configuration error", - Explanation => "'" - . $args{'action'} - . "' not a recognized action." - . " Your RT administrator has misconfigured " - . "the mail aliases which invoke RT", - MIMEObj => $Message ); - $RT::Logger->crit( $args{'action'} . " type unknown for $MessageId" ); - return ( 0, "Configuration error: " . $args{'action'} . " not a recognized action", $Ticket ); - - } - - -return ( 1, "Success", $Ticket ); -} - -eval "require RT::Interface::Email_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email_Vendor.pm}); -eval "require RT::Interface::Email_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email_Local.pm}); - -1; diff --git a/rt/lib/RT/Interface/Email/Auth/GnuPG.pm b/rt/lib/RT/Interface/Email/Auth/GnuPG.pm deleted file mode 100755 index 2dfada755..000000000 --- a/rt/lib/RT/Interface/Email/Auth/GnuPG.pm +++ /dev/null @@ -1,121 +0,0 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2005 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., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# -# 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 }}} -# -package RT::Interface::Email::Auth::GnuPG; -use Mail::GnuPG; - -=head2 GetCurrentUser - -To use the gnupg-secured mail gateway, you need to do the following: - -Set up a gnupgp key directory with a pubring containing only the keys -you care about and specify the following in your SiteConfig.pm - -Set($RT::GPGKeyDir, "/path/to/keyring-directory"); -@RT::MailPlugins = qw(Auth::MailFrom Auth::GnuPG Filter::TakeAction); - - - -=cut - - - -sub GetCurrentUser { - my %args = ( - Message => undef, - RawMessageRef => undef, - CurrentUser => undef, - AuthLevel => undef, - Ticket => undef, - Queue => undef, - Action => undef, - @_ - ); - - my ( $val, $key, $address,$gpg ); - - eval { - - my $parser = RT::EmailParser->new(); - $parser->SmartParseMIMEEntityFromScalar(Message => ${$args{'RawMessageRef'}}, Decode => 0); - $gpg = Mail::GnuPG->new( keydir => $RT::GPGKeyDir ); - my $entity = $parser->Entity; - ( $val, $key, $address ) = $gpg->verify( $parser->Entity); - $RT::Logger->crit("Got $val - $key - $address"); - }; - - if ($@) { - $RT::Logger->crit($@); - } - - unless ($address) { - $RT::Logger->crit( "Couldn't find a valid signature" . join ( "\n", @{ $gpg->{'last_message'} } ) ); - return ( $args{'CurrentUser'}, $args{'AuthLevel'} ); - } - - my @addrs = Mail::Address->parse($address); - $address = $addrs[0]->address(); - - my $CurrentUser = RT::CurrentUser->new(); - $CurrentUser->LoadByEmail($address); - - if ( $CurrentUser->Id ) { - $RT::Logger->crit($address . " authenticated via PGP signature"); - return ( $CurrentUser, 2 ); - } - -} - -eval "require RT::Interface::Email::Auth::GnuPG_Vendor"; -die $@ - if ( $@ - && $@ !~ qr{^Can't locate RT/Interface/Email/Auth/GnuPG_Vendor.pm} ); -eval "require RT::Interface::Email::Auth::GnuPG_Local"; -die $@ - if ( $@ - && $@ !~ qr{^Can't locate RT/Interface/Email/Auth/GnuPG_Local.pm} ); - -1; diff --git a/rt/lib/RT/Interface/Email/Auth/MailFrom.pm b/rt/lib/RT/Interface/Email/Auth/MailFrom.pm deleted file mode 100644 index ef315dd53..000000000 --- a/rt/lib/RT/Interface/Email/Auth/MailFrom.pm +++ /dev/null @@ -1,182 +0,0 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2005 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., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# -# 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 }}} -package RT::Interface::Email::Auth::MailFrom; -use RT::Interface::Email qw(ParseSenderAddressFromHead CreateUser); - -# This is what the ordinary, non-enhanced gateway does at the moment. - -sub GetCurrentUser { - my %args = ( Message => undef, - CurrentUser => undef, - AuthLevel => undef, - Ticket => undef, - Queue => undef, - Action => undef, - @_ ); - - - # We don't need to do any external lookups - my ( $Address, $Name ) = ParseSenderAddressFromHead( $args{'Message'}->head ); - my $CurrentUser = RT::CurrentUser->new(); - $CurrentUser->LoadByEmail($Address); - - unless ( $CurrentUser->Id ) { - $CurrentUser->LoadByName($Address); - } - - if ( $CurrentUser->Id ) { - return ( $CurrentUser, 1 ); - } - - - - # If the user can't be loaded, we may need to create one. Figure out the acl situation. - my $unpriv = RT::Group->new($RT::SystemUser); - $unpriv->LoadSystemInternalGroup('Unprivileged'); - unless ( $unpriv->Id ) { - $RT::Logger->crit( "Auth::MailFrom couldn't find the 'Unprivileged' internal group" ); - return ( $args{'CurrentUser'}, -1 ); - } - - my $everyone = RT::Group->new($RT::SystemUser); - $everyone->LoadSystemInternalGroup('Everyone'); - unless ( $everyone->Id ) { - $RT::Logger->crit( "Auth::MailFrom couldn't find the 'Everyone' internal group"); - return ( $args{'CurrentUser'}, -1 ); - } - - # but before we do that, we need to make sure that the created user would have the right - # to do what we're doing. - if ( $args{'Ticket'} && $args{'Ticket'}->Id ) { - # We have a ticket. that means we're commenting or corresponding - if ( $args{'Action'} =~ /^comment$/i ) { - - # check to see whether "Everyone" or "Unprivileged users" can comment on tickets - unless ( $everyone->PrincipalObj->HasRight( - Object => $args{'Queue'}, - Right => 'CommentOnTicket' - ) - || $unpriv->PrincipalObj->HasRight( - Object => $args{'Queue'}, - Right => 'CommentOnTicket' - ) - ) { - return ( $args{'CurrentUser'}, 0 ); - } - } - elsif ( $args{'Action'} =~ /^correspond$/i ) { - - # check to see whether "Everybody" or "Unprivileged users" can correspond on tickets - unless ( $everyone->PrincipalObj->HasRight(Object => $args{'Queue'}, - Right => 'ReplyToTicket' - ) - || $unpriv->PrincipalObj->HasRight( - Object => $args{'Queue'}, - Right => 'ReplyToTicket' - ) - ) { - return ( $args{'CurrentUser'}, 0 ); - } - - } - elsif ( $args{'Action'} =~ /^take$/i ) { - - # check to see whether "Everybody" or "Unprivileged users" can correspond on tickets - unless ( $everyone->PrincipalObj->HasRight(Object => $args{'Queue'}, - Right => 'OwnTicket' - ) - || $unpriv->PrincipalObj->HasRight( - Object => $args{'Queue'}, - Right => 'OwnTicket' - ) - ) { - return ( $args{'CurrentUser'}, 0 ); - } - - } - elsif ( $args{'Action'} =~ /^resolve$/i ) { - - # check to see whether "Everybody" or "Unprivileged users" can correspond on tickets - unless ( $everyone->PrincipalObj->HasRight(Object => $args{'Queue'}, - Right => 'ModifyTicket' - ) - || $unpriv->PrincipalObj->HasRight( - Object => $args{'Queue'}, - Right => 'ModifyTicket' - ) - ) { - return ( $args{'CurrentUser'}, 0 ); - } - - } - else { - return ( $args{'CurrentUser'}, 0 ); - } - } - - # We're creating a ticket - elsif ( $args{'Queue'} && $args{'Queue'}->Id ) { - - # check to see whether "Everybody" or "Unprivileged users" can create tickets in this queue - unless ( $everyone->PrincipalObj->HasRight( Object => $args{'Queue'}, - Right => 'CreateTicket' ) - ) { - return ( $args{'CurrentUser'}, 0 ); - } - - } - - $CurrentUser = CreateUser( undef, $Address, $Name, $Address, $args{'Message'} ); - - return ( $CurrentUser, 1 ); -} - -eval "require RT::Interface::Email::Auth::MailFrom_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Auth/MailFrom_Vendor.pm}); -eval "require RT::Interface::Email::Auth::MailFrom_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Auth/MailFrom_Local.pm}); - -1; diff --git a/rt/lib/RT/Interface/Email/Filter/SpamAssassin.pm b/rt/lib/RT/Interface/Email/Filter/SpamAssassin.pm deleted file mode 100644 index c552d76e6..000000000 --- a/rt/lib/RT/Interface/Email/Filter/SpamAssassin.pm +++ /dev/null @@ -1,94 +0,0 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2005 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., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# -# 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 }}} -package RT::Interface::Email::Filter::SpamAssassin; - -use Mail::SpamAssassin; -my $spamtest = Mail::SpamAssassin->new(); - -sub GetCurrentUser { - my %args = ( - Message => undef, - CurrentUser => undef, - AuthLevel => undef, - @_ - ); - my $status = $spamtest->check( $args{'Message'} ); - return ( $args{'CurrentUser'}, $args{'AuthLevel'} ) - unless $status->is_spam(); - - eval { $status->rewrite_mail() }; - if ( $status->get_hits > $status->get_required_hits() * 1.5 ) { - - # Spammy indeed - return ( $args{'CurrentUser'}, -1 ); - } - return ( $args{'CurrentUser'}, $args{'AuthLevel'} ); - -} - -=head1 NAME - -RT::Interface::Email::Filter::SpamAssassin - Spam filter for RT - -=head1 SYNOPSIS - - @RT::MailPlugins = ("Filter::SpamAssassin", ...); - -=head1 DESCRIPTION - -This plugin checks to see if an incoming mail is spam (using -C<spamassassin>) and if so, rewrites its headers. If the mail is very -definitely spam - 1.5x more hits than required - then it is dropped on -the floor; otherwise, it is passed on as normal. - -=cut - -eval "require RT::Interface::Email::Filter::SpamAssassin_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Filter/SpamAssassin_Vendor.pm}); -eval "require RT::Interface::Email::Filter::SpamAssassin_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Filter/SpamAssassin_Local.pm}); - -1; diff --git a/rt/lib/RT/Interface/REST.pm b/rt/lib/RT/Interface/REST.pm deleted file mode 100644 index 279ddf4b3..000000000 --- a/rt/lib/RT/Interface/REST.pm +++ /dev/null @@ -1,274 +0,0 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2005 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., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# -# 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 }}} -# lib/RT/Interface/REST.pm -# - -package RT::Interface::REST; -use strict; -use RT; - -BEGIN { - use Exporter (); - use vars qw($VERSION @ISA @EXPORT); - - $VERSION = do { my @r = (q$Revision: 1.1.1.3 $ =~ /\d+/g); sprintf "%d."."%02d"x$#r, @r }; - - @ISA = qw(Exporter); - @EXPORT = qw(expand_list form_parse form_compose vpush vsplit); -} - -my $field = '[a-zA-Z][a-zA-Z0-9_-]*'; - -sub expand_list { - my ($list) = @_; - my ($elt, @elts, %elts); - - foreach $elt (split /,/, $list) { - if ($elt =~ /^(\d+)-(\d+)$/) { push @elts, ($1..$2) } - else { push @elts, $elt } - } - - @elts{@elts}=(); - return sort {$a<=>$b} keys %elts; -} - -# Returns a reference to an array of parsed forms. -sub form_parse { - my $state = 0; - my @forms = (); - my @lines = split /\n/, $_[0]; - my ($c, $o, $k, $e) = ("", [], {}, ""); - - LINE: - while (@lines) { - my $line = shift @lines; - - next LINE if $line eq ''; - - if ($line eq '--') { - # We reached the end of one form. We'll ignore it if it was - # empty, and store it otherwise, errors and all. - if ($e || $c || @$o) { - push @forms, [ $c, $o, $k, $e ]; - $c = ""; $o = []; $k = {}; $e = ""; - } - $state = 0; - } - elsif ($state != -1) { - if ($state == 0 && $line =~ /^#/) { - # Read an optional block of comments (only) at the start - # of the form. - $state = 1; - $c = $line; - while (@lines && $lines[0] =~ /^#/) { - $c .= "\n".shift @lines; - } - $c .= "\n"; - } - elsif ($state <= 1 && $line =~ /^($field):(?:\s+(.*))?$/) { - # Read a field: value specification. - my $f = $1; - my @v = ($2 || ()); - - # Read continuation lines, if any. - while (@lines && ($lines[0] eq '' || $lines[0] =~ /^\s+/)) { - push @v, shift @lines; - } - pop @v while (@v && $v[-1] eq ''); - - # Strip longest common leading indent from text. - my ($ws, $ls) = (""); - foreach $ls (map {/^(\s+)/} @v[1..$#v]) { - $ws = $ls if (!$ws || length($ls) < length($ws)); - } - s/^$ws// foreach @v; - - push(@$o, $f) unless exists $k->{$f}; - vpush($k, $f, join("\n", @v)); - - $state = 1; - } - elsif ($line !~ /^#/) { - # We've found a syntax error, so we'll reconstruct the - # form parsed thus far, and add an error marker. (>>) - $state = -1; - $e = form_compose([[ "", $o, $k, "" ]]); - $e.= $line =~ /^>>/ ? "$line\n" : ">> $line\n"; - } - } - else { - # We saw a syntax error earlier, so we'll accumulate the - # contents of this form until the end. - $e .= "$line\n"; - } - } - push(@forms, [ $c, $o, $k, $e ]) if ($e || $c || @$o); - - my $l; - foreach $l (keys %$k) { - $k->{$l} = vsplit($k->{$l}) if (ref $k->{$l} eq 'ARRAY'); - } - - return \@forms; -} - -# Returns text representing a set of forms. -sub form_compose { - my ($forms) = @_; - my (@text, $form); - - foreach $form (@$forms) { - my ($c, $o, $k, $e) = @$form; - my $text = ""; - - if ($c) { - $c =~ s/\n*$/\n/; - $text = "$c\n"; - } - if ($e) { - $text .= $e; - } - elsif ($o) { - my (@lines, $key); - - foreach $key (@$o) { - my ($line, $sp, $v); - my @values = (ref $k->{$key} eq 'ARRAY') ? - @{ $k->{$key} } : - $k->{$key}; - - $sp = " "x(length("$key: ")); - $sp = " "x4 if length($sp) > 16; - - foreach $v (@values) { - if ($v =~ /\n/) { - $v =~ s/^/$sp/gm; - $v =~ s/^$sp//; - - if ($line) { - push @lines, "$line\n\n"; - $line = ""; - } - elsif (@lines && $lines[-1] !~ /\n\n$/) { - $lines[-1] .= "\n"; - } - push @lines, "$key: $v\n\n"; - } - elsif ($line && - length($line)+length($v)-rindex($line, "\n") >= 70) - { - $line .= ",\n$sp$v"; - } - else { - $line = $line ? "$line, $v" : "$key: $v"; - } - } - - $line = "$key:" unless @values; - if ($line) { - if ($line =~ /\n/) { - if (@lines && $lines[-1] !~ /\n\n$/) { - $lines[-1] .= "\n"; - } - $line .= "\n"; - } - push @lines, "$line\n"; - } - } - - $text .= join "", @lines; - } - else { - chomp $text; - } - push @text, $text; - } - - return join "\n--\n\n", @text; -} - -# Add a value to a (possibly multi-valued) hash key. -sub vpush { - my ($hash, $key, $val) = @_; - my @val = ref $val eq 'ARRAY' ? @$val : $val; - - if (exists $hash->{$key}) { - unless (ref $hash->{$key} eq 'ARRAY') { - my @v = $hash->{$key} ne '' ? $hash->{$key} : (); - $hash->{$key} = \@v; - } - push @{ $hash->{$key} }, @val; - } - else { - $hash->{$key} = $val; - } -} - -# "Normalise" a hash key that's known to be multi-valued. -sub vsplit { - my ($val) = @_; - my ($line, $word, @words); - - foreach $line (map {split /\n/} (ref $val eq 'ARRAY') ? @$val : $val) - { - # XXX: This should become a real parser, à la Text::ParseWords. - $line =~ s/^\s+//; - $line =~ s/\s+$//; - push @words, split /\s*,\s*/, $line; - } - - return \@words; -} - -1; - -=head1 NAME - - RT::Interface::REST - helper functions for the REST interface. - -=head1 SYNOPSIS - - Only the REST should use this module. diff --git a/rt/lib/RT/Interface/Web.pm b/rt/lib/RT/Interface/Web.pm deleted file mode 100644 index 5097f54a4..000000000 --- a/rt/lib/RT/Interface/Web.pm +++ /dev/null @@ -1,1377 +0,0 @@ -# BEGIN LICENSE BLOCK -# -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> -# -# (Except where explictly superceded by other copyright notices) -# -# 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. -# -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. -# -# -# END LICENSE BLOCK -## Portions Copyright 2000 Tobias Brox <tobix@fsck.com> - -## This is a library of static subs to be used by the Mason web -## interface to RT - - -=head1 NAME - -RT::Interface::Web - -=begin testing - -use_ok(RT::Interface::Web); - -=end testing - -=cut - - -package RT::Interface::Web; -use strict; - - - - - -# {{{ sub NewApacheHandler - -=head2 NewApacheHandler - - Takes extra options to pass to HTML::Mason::ApacheHandler->new - Returns a new Mason::ApacheHandler object - -=cut - -sub NewApacheHandler { - require HTML::Mason::ApacheHandler; - my $ah = new HTML::Mason::ApacheHandler( - - comp_root => [ - [ local => $RT::MasonLocalComponentRoot ], - [ standard => $RT::MasonComponentRoot ] - ], - args_method => "CGI", - default_escape_flags => 'h', - allow_globals => [qw(%session)], - data_dir => "$RT::MasonDataDir", - @_ - ); - - $ah->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 ); - - return ($ah); -} - -# }}} - -# {{{ sub NewCGIHandler - -=head2 NewCGIHandler - - Returns a new Mason::CGIHandler object - -=cut - -sub NewCGIHandler { - my %args = ( - @_ - ); - - my $handler = HTML::Mason::CGIHandler->new( - comp_root => [ - [ local => $RT::MasonLocalComponentRoot ], - [ standard => $RT::MasonComponentRoot ] - ], - data_dir => "$RT::MasonDataDir", - default_escape_flags => 'h', - allow_globals => [qw(%session)] - ); - - - $handler->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 ); - - - return ($handler); - -} -# }}} - - -# {{{ EscapeUTF8 - -=head2 EscapeUTF8 SCALARREF - -does a css-busting but minimalist escaping of whatever html you're passing in. - -=cut - -sub EscapeUTF8 { - my $ref = shift; - my $val = $$ref; - use bytes; - $val =~ s/&/&/g; - $val =~ s/</</g; - $val =~ s/>/>/g; - $val =~ s/\(/(/g; - $val =~ s/\)/)/g; - $val =~ s/"/"/g; - $val =~ s/'/'/g; - $$ref = $val; - Encode::_utf8_on($$ref); - -} - -# }}} - - -package HTML::Mason::Commands; -use strict; -use vars qw/$r $m %session/; - - -# {{{ loc - -=head2 loc ARRAY - -loc is a nice clean global routine which calls $session{'CurrentUser'}->loc() -with whatever it's called with. If there is no $session{'CurrentUser'}, -it creates a temporary user, so we have something to get a localisation handle -through - -=cut - -sub loc { - - if ($session{'CurrentUser'} && - UNIVERSAL::can($session{'CurrentUser'}, 'loc')){ - return($session{'CurrentUser'}->loc(@_)); - } - else { - my $u = RT::CurrentUser->new($RT::SystemUser); - return ($u->loc(@_)); - } -} - -# }}} - - -# {{{ loc_fuzzy - -=head2 loc_fuzzy STRING - -loc_fuzzy is for handling localizations of messages that may already -contain interpolated variables, typically returned from libraries -outside RT's control. It takes the message string and extracts the -variable array automatically by matching against the candidate entries -inside the lexicon file. - -=cut - -sub loc_fuzzy { - my $msg = shift; - - if ($session{'CurrentUser'} && - UNIVERSAL::can($session{'CurrentUser'}, 'loc')){ - return($session{'CurrentUser'}->loc_fuzzy($msg)); - } - else { - my $u = RT::CurrentUser->new($RT::SystemUser); - return ($u->loc_fuzzy($msg)); - } -} - -# }}} - - -# {{{ sub Abort -# Error - calls Error and aborts -sub Abort { - - if ($session{'ErrorDocument'} && - $session{'ErrorDocumentType'}) { - $r->content_type($session{'ErrorDocumentType'}); - $m->comp($session{'ErrorDocument'} , Why => shift); - $m->abort; - } - else { - $m->comp("/Elements/Error" , Why => shift); - $m->abort; - } -} - -# }}} - -# {{{ sub CreateTicket - -=head2 CreateTicket ARGS - -Create a new ticket, using Mason's %ARGS. returns @results. - -=cut - -sub CreateTicket { - my %ARGS = (@_); - - my (@Actions); - - my $Ticket = new RT::Ticket( $session{'CurrentUser'} ); - - my $Queue = new RT::Queue( $session{'CurrentUser'} ); - unless ( $Queue->Load( $ARGS{'Queue'} ) ) { - Abort('Queue not found'); - } - - unless ( $Queue->CurrentUserHasRight('CreateTicket') ) { - Abort('You have no permission to create tickets in that queue.'); - } - - my $due = new RT::Date( $session{'CurrentUser'} ); - $due->Set( Format => 'unknown', Value => $ARGS{'Due'} ); - my $starts = new RT::Date( $session{'CurrentUser'} ); - $starts->Set( Format => 'unknown', Value => $ARGS{'Starts'} ); - - my @Requestors = split ( /\s*,\s*/, $ARGS{'Requestors'} ); - my @Cc = split ( /\s*,\s*/, $ARGS{'Cc'} ); - my @AdminCc = split ( /\s*,\s*/, $ARGS{'AdminCc'} ); - - my $MIMEObj = MakeMIMEEntity( - Subject => $ARGS{'Subject'}, - From => $ARGS{'From'}, - Cc => $ARGS{'Cc'}, - Body => $ARGS{'Content'}, - ); - - if ($ARGS{'Attachments'}) { - $MIMEObj->make_multipart; - $MIMEObj->add_part($_) foreach values %{$ARGS{'Attachments'}}; - } - - my %create_args = ( - Queue => $ARGS{'Queue'}, - Owner => $ARGS{'Owner'}, - InitialPriority => $ARGS{'InitialPriority'}, - FinalPriority => $ARGS{'FinalPriority'}, - TimeLeft => $ARGS{'TimeLeft'}, - TimeEstimated => $ARGS{'TimeEstimated'}, - TimeWorked => $ARGS{'TimeWorked'}, - Requestor => \@Requestors, - Cc => \@Cc, - AdminCc => \@AdminCc, - Subject => $ARGS{'Subject'}, - Status => $ARGS{'Status'}, - Due => $due->ISO, - Starts => $starts->ISO, - MIMEObj => $MIMEObj - ); - foreach my $arg (%ARGS) { - if ($arg =~ /^CustomField-(\d+)(.*?)$/) { - next if ($arg =~ /-Magic$/); - $create_args{"CustomField-".$1} = $ARGS{"$arg"}; - } - } - my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args); - unless ( $id && $Trans ) { - Abort($ErrMsg); - } - my @linktypes = qw( DependsOn MemberOf RefersTo ); - - foreach my $linktype (@linktypes) { - foreach my $luri ( split ( / /, $ARGS{"new-$linktype"} ) ) { - $luri =~ s/\s*$//; # Strip trailing whitespace - my ( $val, $msg ) = $Ticket->AddLink( - Target => $luri, - Type => $linktype - ); - push ( @Actions, $msg ) unless ($val); - } - - foreach my $luri ( split ( / /, $ARGS{"$linktype-new"} ) ) { - my ( $val, $msg ) = $Ticket->AddLink( - Base => $luri, - Type => $linktype - ); - - push ( @Actions, $msg ) unless ($val); - } - } - - push ( @Actions, split("\n", $ErrMsg) ); - unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) { - Abort( "No permission to view newly created ticket #" - . $Ticket->id . "." ); - } - return ( $Ticket, @Actions ); - -} - -# }}} - -# {{{ sub LoadTicket - loads a ticket - -=head2 LoadTicket id - -Takes a ticket id as its only variable. if it's handed an array, it takes -the first value. - -Returns an RT::Ticket object as the current user. - -=cut - -sub LoadTicket { - my $id = shift; - - if ( ref($id) eq "ARRAY" ) { - $id = $id->[0]; - } - - unless ($id) { - Abort("No ticket specified"); - } - - my $Ticket = RT::Ticket->new( $session{'CurrentUser'} ); - $Ticket->Load($id); - unless ( $Ticket->id ) { - Abort("Could not load ticket $id"); - } - return $Ticket; -} - -# }}} - -# {{{ sub ProcessUpdateMessage - -sub ProcessUpdateMessage { - - #TODO document what else this takes. - my %args = ( - ARGSRef => undef, - Actions => undef, - TicketObj => undef, - @_ - ); - - #Make the update content have no 'weird' newlines in it - if ( $args{ARGSRef}->{'UpdateContent'} ) { - - if ( - $args{ARGSRef}->{'UpdateSubject'} eq $args{'TicketObj'}->Subject() ) - { - $args{ARGSRef}->{'UpdateSubject'} = undef; - } - - my $Message = MakeMIMEEntity( - Subject => $args{ARGSRef}->{'UpdateSubject'}, - Body => $args{ARGSRef}->{'UpdateContent'}, - ); - - if ($args{ARGSRef}->{'UpdateAttachments'}) { - $Message->make_multipart; - $Message->add_part($_) foreach values %{$args{ARGSRef}->{'UpdateAttachments'}}; - } - - ## TODO: Implement public comments - if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) { - my ( $Transaction, $Description ) = $args{TicketObj}->Comment( - CcMessageTo => $args{ARGSRef}->{'UpdateCc'}, - BccMessageTo => $args{ARGSRef}->{'UpdateBcc'}, - MIMEObj => $Message, - TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'} - ); - push ( @{ $args{Actions} }, $Description ); - } - elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) { - my ( $Transaction, $Description ) = $args{TicketObj}->Correspond( - CcMessageTo => $args{ARGSRef}->{'UpdateCc'}, - BccMessageTo => $args{ARGSRef}->{'UpdateBcc'}, - MIMEObj => $Message, - TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'} - ); - push ( @{ $args{Actions} }, $Description ); - } - else { - push ( @{ $args{'Actions'} }, - loc("Update type was neither correspondence nor comment."). - " ". - loc("Update not recorded.") - ); - } - } -} - -# }}} - -# {{{ sub MakeMIMEEntity - -=head2 MakeMIMEEntity PARAMHASH - -Takes a paramhash Subject, Body and AttachmentFieldName. - - Returns a MIME::Entity. - -=cut - -sub MakeMIMEEntity { - - #TODO document what else this takes. - my %args = ( - Subject => undef, - From => undef, - Cc => undef, - Body => undef, - AttachmentFieldName => undef, - map Encode::encode_utf8($_), @_, - ); - - #Make the update content have no 'weird' newlines in it - - $args{'Body'} =~ s/\r\n/\n/gs; - my $Message; - { - # MIME::Head is not happy in utf-8 domain. This only happens - # when processing an incoming email (so far observed). - no utf8; - use bytes; - $Message = MIME::Entity->build( - Subject => $args{'Subject'} || "", - From => $args{'From'}, - Cc => $args{'Cc'}, - Data => [ $args{'Body'} ] - ); - } - - my $cgi_object = $m->cgi_object; - - if (my $filehandle = $cgi_object->upload( $args{'AttachmentFieldName'} ) ) { - - - - use File::Temp qw(tempfile tempdir); - - #foreach my $filehandle (@filenames) { - - my ( $fh, $temp_file ) = tempfile(); - - binmode $fh; #thank you, windows - my ($buffer); - while ( my $bytesread = read( $filehandle, $buffer, 4096 ) ) { - print $fh $buffer; - } - - my $uploadinfo = $cgi_object->uploadInfo($filehandle); - - # Prefer the cached name first over CGI.pm stringification. - my $filename = $RT::Mason::CGI::Filename; - $filename = "$filehandle" unless defined($filename); - - $filename =~ s#^.*[\\/]##; - - $Message->attach( - Path => $temp_file, - Filename => $filename, - Type => $uploadinfo->{'Content-Type'}, - ); - close($fh); - - # } - - } - - $Message->make_singlepart(); - RT::I18N::SetMIMEEntityToUTF8($Message); # convert text parts into utf-8 - - return ($Message); - -} - -# }}} - -# {{{ sub ProcessSearchQuery - -=head2 ProcessSearchQuery - - Takes a form such as the one filled out in webrt/Search/Elements/PickRestriction and turns it into something that RT::Tickets can understand. - -TODO Doc exactly what comes in the paramhash - - -=cut - -sub ProcessSearchQuery { - my %args = @_; - - ## TODO: The only parameter here is %ARGS. Maybe it would be - ## cleaner to load this parameter as $ARGS, and use $ARGS->{...} - ## instead of $args{ARGS}->{...} ? :) - - #Searches are sticky. - if ( defined $session{'tickets'} ) { - - # Reset the old search - $session{'tickets'}->GotoFirstItem; - } - else { - - # Init a new search - $session{'tickets'} = RT::Tickets->new( $session{'CurrentUser'} ); - } - - #Import a bookmarked search if we have one - if ( defined $args{ARGS}->{'Bookmark'} ) { - $session{'tickets'}->ThawLimits( $args{ARGS}->{'Bookmark'} ); - } - - # {{{ Goto next/prev page - if ( $args{ARGS}->{'GotoPage'} eq 'Next' ) { - $session{'tickets'}->NextPage; - } - elsif ( $args{ARGS}->{'GotoPage'} eq 'Prev' ) { - $session{'tickets'}->PrevPage; - } - elsif ( $args{ARGS}->{'GotoPage'} > 0 ) { - $session{'tickets'}->GotoPage( $args{ARGS}->{GotoPage} - 1 ); - } - - # }}} - - # {{{ Deal with limiting the search - - if ( $args{ARGS}->{'RefreshSearchInterval'} ) { - $session{'tickets_refresh_interval'} = - $args{ARGS}->{'RefreshSearchInterval'}; - } - - if ( $args{ARGS}->{'TicketsSortBy'} ) { - $session{'tickets_sort_by'} = $args{ARGS}->{'TicketsSortBy'}; - $session{'tickets_sort_order'} = $args{ARGS}->{'TicketsSortOrder'}; - $session{'tickets'}->OrderBy( - FIELD => $args{ARGS}->{'TicketsSortBy'}, - ORDER => $args{ARGS}->{'TicketsSortOrder'} - ); - } - - # }}} - - # {{{ Set the query limit - if ( defined $args{ARGS}->{'RowsPerPage'} ) { - $RT::Logger->debug( - "limiting to " . $args{ARGS}->{'RowsPerPage'} . " rows" ); - - $session{'tickets_rows_per_page'} = $args{ARGS}->{'RowsPerPage'}; - $session{'tickets'}->RowsPerPage( $args{ARGS}->{'RowsPerPage'} ); - } - - # }}} - # {{{ Limit priority - if ( $args{ARGS}->{'ValueOfPriority'} ne '' ) { - $session{'tickets'}->LimitPriority( - VALUE => $args{ARGS}->{'ValueOfPriority'}, - OPERATOR => $args{ARGS}->{'PriorityOp'} - ); - } - - # }}} - # {{{ Limit owner - if ( $args{ARGS}->{'ValueOfOwner'} ne '' ) { - $session{'tickets'}->LimitOwner( - VALUE => $args{ARGS}->{'ValueOfOwner'}, - OPERATOR => $args{ARGS}->{'OwnerOp'} - ); - } - - # }}} - # {{{ Limit requestor email - - if ( $args{ARGS}->{'ValueOfRequestor'} ne '' ) { - my $alias = $session{'tickets'}->LimitRequestor( - VALUE => $args{ARGS}->{'ValueOfRequestor'}, - OPERATOR => $args{ARGS}->{'RequestorOp'}, - ); - - } - - # }}} - # {{{ Limit Queue - if ( $args{ARGS}->{'ValueOfQueue'} ne '' ) { - $session{'tickets'}->LimitQueue( - VALUE => $args{ARGS}->{'ValueOfQueue'}, - OPERATOR => $args{ARGS}->{'QueueOp'} - ); - } - - # }}} - # {{{ Limit Status - if ( $args{ARGS}->{'ValueOfStatus'} ne '' ) { - if ( ref( $args{ARGS}->{'ValueOfStatus'} ) ) { - foreach my $value ( @{ $args{ARGS}->{'ValueOfStatus'} } ) { - $session{'tickets'}->LimitStatus( - VALUE => $value, - OPERATOR => $args{ARGS}->{'StatusOp'}, - ); - } - } - else { - $session{'tickets'}->LimitStatus( - VALUE => $args{ARGS}->{'ValueOfStatus'}, - OPERATOR => $args{ARGS}->{'StatusOp'}, - ); - } - - } - - # }}} - # {{{ Limit Subject - if ( $args{ARGS}->{'ValueOfSubject'} ne '' ) { - my $val = $args{ARGS}->{'ValueOfSubject'}; - if ($args{ARGS}->{'SubjectOp'} =~ /like/) { - $val = "%".$val."%"; - } - $session{'tickets'}->LimitSubject( - VALUE => $val, - OPERATOR => $args{ARGS}->{'SubjectOp'}, - ); - } - - # }}} - # {{{ Limit Dates - if ( $args{ARGS}->{'ValueOfDate'} ne '' ) { - my $date = ParseDateToISO( $args{ARGS}->{'ValueOfDate'} ); - $args{ARGS}->{'DateType'} =~ s/_Date$//; - - if ( $args{ARGS}->{'DateType'} eq 'Updated' ) { - $session{'tickets'}->LimitTransactionDate( - VALUE => $date, - OPERATOR => $args{ARGS}->{'DateOp'}, - ); - } - else { - $session{'tickets'}->LimitDate( FIELD => $args{ARGS}->{'DateType'}, - VALUE => $date, - OPERATOR => $args{ARGS}->{'DateOp'}, - ); - } - } - - # }}} - # {{{ Limit Content - if ( $args{ARGS}->{'ValueOfAttachmentField'} ne '' ) { - my $val = $args{ARGS}->{'ValueOfAttachmentField'}; - if ($args{ARGS}->{'AttachmentFieldOp'} =~ /like/) { - $val = "%".$val."%"; - } - $session{'tickets'}->Limit( - FIELD => $args{ARGS}->{'AttachmentField'}, - VALUE => $val, - OPERATOR => $args{ARGS}->{'AttachmentFieldOp'}, - ); - } - - # }}} - - # {{{ Limit CustomFields - - foreach my $arg ( keys %{ $args{ARGS} } ) { - my $id; - if ( $arg =~ /^CustomField(\d+)$/ ) { - $id = $1; - } - else { - next; - } - next unless ( $args{ARGS}->{$arg} ); - - my $form = $args{ARGS}->{$arg}; - my $oper = $args{ARGS}->{ "CustomFieldOp" . $id }; - foreach my $value ( ref($form) ? @{$form} : ($form) ) { - my $quote = 1; - if ($oper =~ /like/i) { - $value = "%".$value."%"; - } - if ( $value =~ /^null$/i ) { - - #Don't quote the string 'null' - $quote = 0; - - # Convert the operator to something apropriate for nulls - $oper = 'IS' if ( $oper eq '=' ); - $oper = 'IS NOT' if ( $oper eq '!=' ); - } - $session{'tickets'}->LimitCustomField( CUSTOMFIELD => $id, - OPERATOR => $oper, - QUOTEVALUE => $quote, - VALUE => $value ); - } - } - - # }}} - - -} - -# }}} - -# {{{ sub ParseDateToISO - -=head2 ParseDateToISO - -Takes a date in an arbitrary format. -Returns an ISO date and time in GMT - -=cut - -sub ParseDateToISO { - my $date = shift; - - my $date_obj = RT::Date->new($session{'CurrentUser'}); - $date_obj->Set( - Format => 'unknown', - Value => $date - ); - return ( $date_obj->ISO ); -} - -# }}} - -# {{{ sub Config -# TODO: This might eventually read the cookies, user configuration -# information from the DB, queue configuration information from the -# DB, etc. - -sub Config { - my $args = shift; - my $key = shift; - return $args->{$key} || $RT::WebOptions{$key}; -} - -# }}} - -# {{{ sub ProcessACLChanges - -sub ProcessACLChanges { - my $ARGSref = shift; - - my %ARGS = %$ARGSref; - - my ( $ACL, @results ); - - - foreach my $arg (keys %ARGS) { - if ($arg =~ /GrantRight-(\d+)-(.*?)-(\d+)$/) { - my $principal_id = $1; - my $object_type = $2; - my $object_id = $3; - my $rights = $ARGS{$arg}; - - my $principal = RT::Principal->new($session{'CurrentUser'}); - $principal->Load($principal_id); - - my $obj; - - if ($object_type eq 'RT::Queue') { - $obj = RT::Queue->new($session{'CurrentUser'}); - $obj->Load($object_id); - } elsif ($object_type eq 'RT::Group') { - $obj = RT::Group->new($session{'CurrentUser'}); - $obj->Load($object_id); - - } elsif ($object_type eq 'RT::System') { - $obj = $RT::System; - } else { - push (@results, loc("System Error"). - loc("Rights could not be granted for [_1]", $object_type)); - next; - } - - my @rights = ref($ARGS{$arg}) eq 'ARRAY' ? @{$ARGS{$arg}} : ($ARGS{$arg}); - foreach my $right (@rights) { - next unless ($right); - my ($val, $msg) = $principal->GrantRight(Object => $obj, Right => $right); - push (@results, $msg); - } - } - elsif ($arg =~ /RevokeRight-(\d+)-(.*?)-(\d+)-(.*?)$/) { - my $principal_id = $1; - my $object_type = $2; - my $object_id = $3; - my $right = $4; - - my $principal = RT::Principal->new($session{'CurrentUser'}); - $principal->Load($principal_id); - next unless ($right); - my $obj; - - if ($object_type eq 'RT::Queue') { - $obj = RT::Queue->new($session{'CurrentUser'}); - $obj->Load($object_id); - } elsif ($object_type eq 'RT::Group') { - $obj = RT::Group->new($session{'CurrentUser'}); - $obj->Load($object_id); - - } elsif ($object_type eq 'RT::System') { - $obj = $RT::System; - } else { - push (@results, loc("System Error"). - loc("Rights could not be revoked for [_1]", $object_type)); - next; - } - my ($val, $msg) = $principal->RevokeRight(Object => $obj, Right => $right); - push (@results, $msg); - } - - - } - - return (@results); - - } - -# }}} - -# {{{ sub UpdateRecordObj - -=head2 UpdateRecordObj ( ARGSRef => \%ARGS, Object => RT::Record, AttributesRef => \@attribs) - -@attribs is a list of ticket fields to check and update if they differ from the B<Object>'s current values. ARGSRef is a ref to HTML::Mason's %ARGS. - -Returns an array of success/failure messages - -=cut - -sub UpdateRecordObject { - my %args = ( - ARGSRef => undef, - AttributesRef => undef, - Object => undef, - AttributePrefix => undef, - @_ - ); - - my (@results); - - my $object = $args{'Object'}; - my $attributes = $args{'AttributesRef'}; - my $ARGSRef = $args{'ARGSRef'}; - foreach my $attribute (@$attributes) { - my $value; - if ( defined $ARGSRef->{$attribute} ) { - $value = $ARGSRef->{$attribute}; - } - elsif ( - defined( $args{'AttributePrefix'} ) - && defined( - $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute } - ) - ) { - $value = $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute }; - - } else { - next; - } - - $value =~ s/\r\n/\n/gs; - - if ($value ne $object->$attribute()){ - - my $method = "Set$attribute"; - my ( $code, $msg ) = $object->$method($value); - - push @results, loc($attribute) . ': ' . loc_fuzzy($msg); -=for loc - "[_1] could not be set to [_2].", # loc - "That is already the current value", # loc - "No value sent to _Set!\n", # loc - "Illegal value for [_1]", # loc - "The new value has been set.", # loc - "No column specified", # loc - "Immutable field", # loc - "Nonexistant field?", # loc - "Invalid data", # loc - "Couldn't find row", # loc - "Missing a primary key?: [_1]", # loc - "Found Object", # loc -=cut - }; - } - return (@results); -} - -# }}} - -# {{{ Sub ProcessCustomFieldUpdates - -sub ProcessCustomFieldUpdates { - my %args = ( - CustomFieldObj => undef, - ARGSRef => undef, - @_ - ); - - my $Object = $args{'CustomFieldObj'}; - my $ARGSRef = $args{'ARGSRef'}; - - my @attribs = qw( Name Type Description Queue SortOrder); - my @results = UpdateRecordObject( - AttributesRef => \@attribs, - Object => $Object, - ARGSRef => $ARGSRef - ); - - if ( $ARGSRef->{ "CustomField-" . $Object->Id . "-AddValue-Name" } ) { - - my ( $addval, $addmsg ) = $Object->AddValue( - Name => - $ARGSRef->{ "CustomField-" . $Object->Id . "-AddValue-Name" }, - Description => $ARGSRef->{ "CustomField-" - . $Object->Id - . "-AddValue-Description" }, - SortOrder => $ARGSRef->{ "CustomField-" - . $Object->Id - . "-AddValue-SortOrder" }, - ); - push ( @results, $addmsg ); - } - my @delete_values = ( - ref $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } eq - 'ARRAY' ) - ? @{ $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } } - : ( $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } ); - foreach my $id (@delete_values) { - next unless defined $id; - my ( $err, $msg ) = $Object->DeleteValue($id); - push ( @results, $msg ); - } - return (@results); -} - -# }}} - -# {{{ sub ProcessTicketBasics - -=head2 ProcessTicketBasics ( TicketObj => $Ticket, ARGSRef => \%ARGS ); - -Returns an array of results messages. - -=cut - -sub ProcessTicketBasics { - - my %args = ( - TicketObj => undef, - ARGSRef => undef, - @_ - ); - - my $TicketObj = $args{'TicketObj'}; - my $ARGSRef = $args{'ARGSRef'}; - - # {{{ Set basic fields - my @attribs = qw( - Subject - FinalPriority - Priority - TimeEstimated - TimeWorked - TimeLeft - Status - Queue - ); - - if ( $ARGSRef->{'Queue'} and ( $ARGSRef->{'Queue'} !~ /^(\d+)$/ ) ) { - my $tempqueue = RT::Queue->new($RT::SystemUser); - $tempqueue->Load( $ARGSRef->{'Queue'} ); - if ( $tempqueue->id ) { - $ARGSRef->{'Queue'} = $tempqueue->Id(); - } - } - - my @results = UpdateRecordObject( - AttributesRef => \@attribs, - Object => $TicketObj, - ARGSRef => $ARGSRef - ); - - # We special case owner changing, so we can use ForceOwnerChange - if ( $ARGSRef->{'Owner'} && ( $TicketObj->Owner != $ARGSRef->{'Owner'} ) ) { - my ($ChownType); - if ( $ARGSRef->{'ForceOwnerChange'} ) { - $ChownType = "Force"; - } - else { - $ChownType = "Give"; - } - - my ( $val, $msg ) = - $TicketObj->SetOwner( $ARGSRef->{'Owner'}, $ChownType ); - push ( @results, $msg ); - } - - # }}} - - return (@results); -} - -# }}} - -# {{{ Sub ProcessTicketCustomFieldUpdates - -sub ProcessTicketCustomFieldUpdates { - my %args = ( - ARGSRef => undef, - @_ - ); - - my @results; - - my $ARGSRef = $args{'ARGSRef'}; - - # Build up a list of tickets that we want to work with - my %tickets_to_mod; - my %custom_fields_to_mod; - foreach my $arg ( keys %{$ARGSRef} ) { - if ( $arg =~ /^Ticket-(\d+)-CustomField-(\d+)-/ ) { - - # For each of those tickets, find out what custom fields we want to work with. - $custom_fields_to_mod{$1}{$2} = 1; - } - } - - # For each of those tickets - foreach my $tick ( keys %custom_fields_to_mod ) { - my $Ticket = RT::Ticket->new( $session{'CurrentUser'} ); - $Ticket->Load($tick); - - # For each custom field - foreach my $cf ( keys %{ $custom_fields_to_mod{$tick} } ) { - - my $CustomFieldObj = RT::CustomField->new($session{'CurrentUser'}); - $CustomFieldObj->LoadById($cf); - - foreach my $arg ( keys %{$ARGSRef} ) { - # since http won't pass in a form element with a null value, we need - # to fake it - if ($arg =~ /^(.*?)-Values-Magic$/ ) { - # We don't care about the magic, if there's really a values element; - next if (exists $ARGSRef->{$1.'-Values'}) ; - - $arg = $1."-Values"; - $ARGSRef->{$1."-Values"} = undef; - - } - next unless ( $arg =~ /^Ticket-$tick-CustomField-$cf-/ ); - my @values = - ( ref( $ARGSRef->{$arg} ) eq 'ARRAY' ) - ? @{ $ARGSRef->{$arg} } - : ( $ARGSRef->{$arg} ); - if ( ( $arg =~ /-AddValue$/ ) || ( $arg =~ /-Value$/ ) ) { - foreach my $value (@values) { - next unless ($value); - my ( $val, $msg ) = $Ticket->AddCustomFieldValue( - Field => $cf, - Value => $value - ); - push ( @results, $msg ); - } - } - elsif ( $arg =~ /-DeleteValues$/ ) { - foreach my $value (@values) { - next unless ($value); - my ( $val, $msg ) = $Ticket->DeleteCustomFieldValue( - Field => $cf, - Value => $value - ); - push ( @results, $msg ); - } - } - elsif ( $arg =~ /-Values$/ and $CustomFieldObj->Type !~ /Entry/) { - my $cf_values = $Ticket->CustomFieldValues($cf); - - my %values_hash; - foreach my $value (@values) { - next unless ($value); - - # build up a hash of values that the new set has - $values_hash{$value} = 1; - - unless ( $cf_values->HasEntry($value) ) { - my ( $val, $msg ) = $Ticket->AddCustomFieldValue( - Field => $cf, - Value => $value - ); - push ( @results, $msg ); - } - - } - while ( my $cf_value = $cf_values->Next ) { - unless ( $values_hash{ $cf_value->Content } == 1 ) { - my ( $val, $msg ) = $Ticket->DeleteCustomFieldValue( - Field => $cf, - Value => $cf_value->Content - ); - push ( @results, $msg); - - } - - } - } - elsif ( $arg =~ /-Values$/ ) { - my $cf_values = $Ticket->CustomFieldValues($cf); - - # keep everything up to the point of difference, delete the rest - my $delete_flag; - foreach my $old_cf (@{$cf_values->ItemsArrayRef}) { - if (!$delete_flag and @values and $old_cf->Content eq $values[0]) { - shift @values; - next; - } - - $delete_flag ||= 1; - $old_cf->Delete; - } - - # now add/replace extra things, if any - foreach my $value (@values) { - my ( $val, $msg ) = $Ticket->AddCustomFieldValue( - Field => $cf, - Value => $value - ); - push ( @results, $msg ); - } - } - else { - push ( @results, "User asked for an unknown update type for custom field " . $cf->Name . " for ticket " . $Ticket->id ); - } - } - } - return (@results); - } -} - -# }}} - -# {{{ sub ProcessTicketWatchers - -=head2 ProcessTicketWatchers ( TicketObj => $Ticket, ARGSRef => \%ARGS ); - -Returns an array of results messages. - -=cut - -sub ProcessTicketWatchers { - my %args = ( - TicketObj => undef, - ARGSRef => undef, - @_ - ); - my (@results); - - my $Ticket = $args{'TicketObj'}; - my $ARGSRef = $args{'ARGSRef'}; - - # {{{ Munge watchers - - foreach my $key ( keys %$ARGSRef ) { - - # {{{ Delete deletable watchers - if ( ( $key =~ /^Ticket-DelWatcher-Type-(.*)-Principal-(\d+)$/ ) ) { - my ( $code, $msg ) = - $Ticket->DeleteWatcher(PrincipalId => $2, - Type => $1); - push @results, $msg; - } - - # Delete watchers in the simple style demanded by the bulk manipulator - elsif ( $key =~ /^Delete(Requestor|Cc|AdminCc)$/ ) { - my ( $code, $msg ) = $Ticket->DeleteWatcher( Type => $ARGSRef->{$key}, PrincipalId => $1 ); - push @results, $msg; - } - - # }}} - - # Add new wathchers by email address - elsif ( ( $ARGSRef->{$key} =~ /^(AdminCc|Cc|Requestor)$/ ) - and ( $key =~ /^WatcherTypeEmail(\d*)$/ ) ) - { - - #They're in this order because otherwise $1 gets clobbered :/ - my ( $code, $msg ) = $Ticket->AddWatcher( - Type => $ARGSRef->{$key}, - Email => $ARGSRef->{ "WatcherAddressEmail" . $1 } - ); - push @results, $msg; - } - - #Add requestors in the simple style demanded by the bulk manipulator - elsif ( $key =~ /^Add(Requestor|Cc|AdminCc)$/ ) { - my ( $code, $msg ) = $Ticket->AddWatcher( - Type => $1, - Email => $ARGSRef->{$key} - ); - push @results, $msg; - } - - # Add new watchers by owner - elsif ( ( $ARGSRef->{$key} =~ /^(AdminCc|Cc|Requestor)$/ ) - and ( $key =~ /^Ticket-AddWatcher-Principal-(\d*)$/ ) ) { - - #They're in this order because otherwise $1 gets clobbered :/ - my ( $code, $msg ) = - $Ticket->AddWatcher( Type => $ARGSRef->{$key}, PrincipalId => $1 ); - push @results, $msg; - } - } - - # }}} - - return (@results); -} - -# }}} - -# {{{ sub ProcessTicketDates - -=head2 ProcessTicketDates ( TicketObj => $Ticket, ARGSRef => \%ARGS ); - -Returns an array of results messages. - -=cut - -sub ProcessTicketDates { - my %args = ( - TicketObj => undef, - ARGSRef => undef, - @_ - ); - - my $Ticket = $args{'TicketObj'}; - my $ARGSRef = $args{'ARGSRef'}; - - my (@results); - - # {{{ Set date fields - my @date_fields = qw( - Told - Resolved - Starts - Started - Due - ); - - #Run through each field in this list. update the value if apropriate - foreach my $field (@date_fields) { - my ( $code, $msg ); - - my $DateObj = RT::Date->new( $session{'CurrentUser'} ); - - #If it's something other than just whitespace - if ( $ARGSRef->{ $field . '_Date' } ne '' ) { - $DateObj->Set( - Format => 'unknown', - Value => $ARGSRef->{ $field . '_Date' } - ); - my $obj = $field . "Obj"; - if ( ( defined $DateObj->Unix ) - and ( $DateObj->Unix ne $Ticket->$obj()->Unix() ) ) - { - my $method = "Set$field"; - my ( $code, $msg ) = $Ticket->$method( $DateObj->ISO ); - push @results, "$msg"; - } - } - } - - # }}} - return (@results); -} - -# }}} - -# {{{ sub ProcessTicketLinks - -=head2 ProcessTicketLinks ( TicketObj => $Ticket, ARGSRef => \%ARGS ); - -Returns an array of results messages. - -=cut - -sub ProcessTicketLinks { - my %args = ( TicketObj => undef, - ARGSRef => undef, - @_ ); - - my $Ticket = $args{'TicketObj'}; - my $ARGSRef = $args{'ARGSRef'}; - - my (@results); - - # Delete links that are gone gone gone. - foreach my $arg ( keys %$ARGSRef ) { - if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) { - my $base = $1; - my $type = $2; - my $target = $3; - - push @results, - "Trying to delete: Base: $base Target: $target Type $type"; - my ( $val, $msg ) = $Ticket->DeleteLink( Base => $base, - Type => $type, - Target => $target ); - - push @results, $msg; - - } - - } - - my @linktypes = qw( DependsOn MemberOf RefersTo ); - - foreach my $linktype (@linktypes) { - if ( $ARGSRef->{ $Ticket->Id . "-$linktype" } ) { - for my $luri ( split ( / /, $ARGSRef->{ $Ticket->Id . "-$linktype" } ) ) { - $luri =~ s/\s*$//; # Strip trailing whitespace - my ( $val, $msg ) = $Ticket->AddLink( Target => $luri, - Type => $linktype ); - push @results, $msg; - } - } - if ( $ARGSRef->{ "$linktype-" . $Ticket->Id } ) { - - for my $luri ( split ( / /, $ARGSRef->{ "$linktype-" . $Ticket->Id } ) ) { - my ( $val, $msg ) = $Ticket->AddLink( Base => $luri, - Type => $linktype ); - - push @results, $msg; - } - } - } - - #Merge if we need to - if ( $ARGSRef->{ $Ticket->Id . "-MergeInto" } ) { - my ( $val, $msg ) = - $Ticket->MergeInto( $ARGSRef->{ $Ticket->Id . "-MergeInto" } ); - push @results, $msg; - } - - return (@results); -} - -# }}} - -eval "require RT::Interface::Web_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web_Vendor.pm}); -eval "require RT::Interface::Web_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web_Local.pm}); - -1; diff --git a/rt/lib/RT/Interface/Web/Handler.pm b/rt/lib/RT/Interface/Web/Handler.pm deleted file mode 100644 index ce9222586..000000000 --- a/rt/lib/RT/Interface/Web/Handler.pm +++ /dev/null @@ -1,221 +0,0 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2005 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., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# -# 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 }}} - -package RT::Interface::Web::Handler; - -use CGI qw/-private_tempfiles/; -use MIME::Entity; -use Text::Wrapper; -use CGI::Cookie; -use Time::ParseDate; -use Time::HiRes; -use HTML::Entities; -use HTML::Scrubber; -use Text::Quoted; -use RT::Interface::Web::Handler; -use File::Path qw( rmtree ); -use File::Glob qw( bsd_glob ); -use File::Spec::Unix; - -sub DefaultHandlerArgs { ( - comp_root => [ - [ local => $RT::MasonLocalComponentRoot ], - [ standard => $RT::MasonComponentRoot ] - ], - default_escape_flags => 'h', - data_dir => "$RT::MasonDataDir", - allow_globals => [qw(%session)], - # Turn off static source if we're in developer mode. - static_source => ($RT::DevelMode ? '0' : '1'), - use_object_files => ($RT::DevelMode ? '0' : '1'), - autoflush => 0 -) }; - -# {{{ sub new - -=head2 new - - Constructs a web handler of the appropriate class. - Takes options to pass to the constructor. - -=cut - -sub new { - my $class = shift; - $class->InitSessionDir; - - if ( $mod_perl::VERSION && $mod_perl::VERSION >= 1.9908 ) { -# require Apache::RequestUtil; -# no warnings 'redefine'; -# my $sub = *Apache::request{CODE}; -# *Apache::request = sub { -# my $r; -# eval { $r = $sub->('Apache'); }; -# -# # warn $@ if $@; -# return $r; -# }; - goto &NewApacheHandler; - } - elsif ($CGI::MOD_PERL) { - goto &NewApacheHandler; - } - else { - goto &NewCGIHandler; - } -} - -sub InitSessionDir { - # Activate the following if running httpd as root (the normal case). - # Resets ownership of all files created by Mason at startup. - # Note that mysql uses DB for sessions, so there's no need to do this. - unless ( $RT::DatabaseType =~ /(?:mysql|Pg)/ ) { - - # Clean up our umask to protect session files - umask(0077); - - if ($CGI::MOD_PERL) { - chown( Apache->server->uid, Apache->server->gid, - $RT::MasonSessionDir ) - if Apache->server->can('uid'); - } - - # Die if WebSessionDir doesn't exist or we can't write to it - stat($RT::MasonSessionDir); - die "Can't read and write $RT::MasonSessionDir" - unless ( ( -d _ ) and ( -r _ ) and ( -w _ ) ); - } - -} - -# }}} - -# {{{ sub NewApacheHandler - -=head2 NewApacheHandler - - Takes extra options to pass to HTML::Mason::ApacheHandler->new - Returns a new Mason::ApacheHandler object - -=cut - -sub NewApacheHandler { - require HTML::Mason::ApacheHandler; - return NewHandler('HTML::Mason::ApacheHandler', args_method => "CGI", @_); -} - -# }}} - -# {{{ sub NewApache2Handler - -=head2 NewApache2Handler - - Takes extra options to pass to MasonX::Apache2Handler->new - Returns a new MasonX::Apache2Handler object - -=cut - -sub NewApache2Handler { - require MasonX::Apache2Handler; - return NewHandler('MasonX::Apache2Handler', args_method => "CGI", @_); -} - -# }}} - -# {{{ sub NewCGIHandler - -=head2 NewCGIHandler - - Returns a new Mason::CGIHandler object - -=cut - -sub NewCGIHandler { - require HTML::Mason::CGIHandler; - return NewHandler('HTML::Mason::CGIHandler', @_); -} - -sub NewHandler { - my $class = shift; - my $handler = $class->new( - DefaultHandlerArgs(), - @_ - ); - - $handler->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 ); - $handler->interp->set_escape( u => \&RT::Interface::Web::EscapeURI ); - return($handler); -} - -=head2 CleanupRequest - -Rollback any uncommitted transaction. -Flush the ACL cache -Flush the searchbuilder query cache - -=cut - -sub CleanupRequest { - - if ( $RT::Handle->TransactionDepth ) { - $RT::Handle->ForceRollback; - $RT::Logger->crit( - "Transaction not committed. Usually indicates a software fault." - . "Data loss may have occurred" ); - } - - # Clean out the ACL cache. the performance impact should be marginal. - # Consistency is imprived, too. - RT::Principal->InvalidateACLCache(); - DBIx::SearchBuilder::Record::Cachable->FlushCache - if ( $RT::WebFlushDbCacheEveryRequest - and UNIVERSAL::can( - 'DBIx::SearchBuilder::Record::Cachable' => 'FlushCache' ) ); - -} -# }}} - -1; diff --git a/rt/lib/RT/Interface/Web/QueryBuilder.pm b/rt/lib/RT/Interface/Web/QueryBuilder.pm deleted file mode 100755 index b7526b30a..000000000 --- a/rt/lib/RT/Interface/Web/QueryBuilder.pm +++ /dev/null @@ -1,56 +0,0 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2005 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., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# -# 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 }}} -package RT::Interface::Web::QueryBuilder; - -use strict; -use warnings; - -eval "require RT::Interface::Web::QueryBuilder_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web/QueryBuilder_Vendor.pm}); -eval "require RT::Interface::Web::QueryBuilder_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web/QueryBuilder_Local.pm}); - -1; diff --git a/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm b/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm deleted file mode 100755 index 67b728339..000000000 --- a/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm +++ /dev/null @@ -1,245 +0,0 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2005 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., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# -# 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 }}} -package RT::Interface::Web::QueryBuilder::Tree; - -use strict; -use warnings; - -use base qw/Tree::Simple/; - -=head1 NAME - - RT::Interface::Web::QueryBuilder::Tree - subclass of Tree::Simple used in Query Builder - -=head1 DESCRIPTION - -This class provides support functionality for the Query Builder (Search/Build.html). -It is a subclass of L<Tree::Simple>. - -=head1 METHODS - -=head2 TraversePrePost PREFUNC POSTFUNC - -Traverses the tree depth-first. Before processing the node's children, -calls PREFUNC with the node as its argument; after processing all of the -children, calls POSTFUNC with the node as its argument. - -(Note that unlike Tree::Simple's C<traverse>, it actually calls its functions -on the root node passed to it.) - -=cut - -sub TraversePrePost { - my ($self, $prefunc, $postfunc) = @_; - - $prefunc->($self); - - foreach my $child ($self->getAllChildren()) { - $child->TraversePrePost($prefunc, $postfunc); - } - - $postfunc->($self); -} - -=head2 GetReferencedQueues - -Returns a hash reference with keys each queue name referenced in a clause in -the key (even if it's "Queue != 'Foo'"), and values all 1. - -=cut - -sub GetReferencedQueues { - my $self = shift; - - my $queues = {}; - - $self->traverse( - sub { - my $node = shift; - - return if $node->isRoot; - - my $clause = $node->getNodeValue(); - - if ( ref($clause) and $clause->{Key} eq 'Queue' ) { - $queues->{ $clause->{Value} } = 1; - }; - } - ); - - return $queues; -} - -=head2 GetQueryAndOptionList SELECTED_NODES - -Given an array reference of tree nodes that have been selected by the user, -traverses the tree and returns the equivalent SQL query and a list of hashes -representing the "clauses" select option list. Each has contains the keys -TEXT, INDEX, SELECTED, and DEPTH. TEXT is the displayed text of the option -(including parentheses, not including indentation); INDEX is the 0-based -index of the option in the list (also used as its CGI parameter); SELECTED -is either 'SELECTED' or '', depending on whether the node corresponding -to the select option was in the SELECTED_NODES list; and DEPTH is the -level of indentation for the option. - -=cut - -sub GetQueryAndOptionList { - my $self = shift; - my $selected_nodes = shift; - - my $optionlist = []; - - my $i = 0; - - $self->TraversePrePost( - sub { # This is called before recursing to the node's children. - my $node = shift; - - return if $node->isRoot or $node->getParent->isRoot; - - my $clause = $node->getNodeValue(); - my $str = ' '; - my $aggregator_context = $node->getParent()->getNodeValue(); - $str = $aggregator_context . " " if $node->getIndex() > 0; - - if ( ref($clause) ) { # ie, it's a leaf - $str .= - $clause->{Key} . " " . $clause->{Op} . " " . $clause->{Value}; - } - - unless ($node->getParent->getParent->isRoot) { - # used to check !ref( $parent->getNodeValue() ) ) - if ( $node->getIndex() == 0 ) { - $str = '( ' . $str; - } - } - - push @$optionlist, { - TEXT => $str, - INDEX => $i, - SELECTED => (grep { $_ == $node } @$selected_nodes) ? 'SELECTED' : '', - DEPTH => $node->getDepth() - 1, - }; - - $i++; - }, sub { - # This is called after recursing to the node's children. - my $node = shift; - - return if $node->isRoot or $node->getParent->isRoot or $node->getParent->getParent->isRoot; - - # Only do this for the rightmost child. - return unless $node->getIndex == $node->getParent->getChildCount - 1; - - $optionlist->[-1]{TEXT} .= ' )'; - } - ); - - return (join ' ', map { $_->{TEXT} } @$optionlist), $optionlist; -} - -=head2 PruneChildLessAggregators - -If tree manipulation has left it in a state where there are ANDs, ORs, -or parenthesizations with no children, get rid of them. - -=cut - -sub PruneChildlessAggregators { - my $self = shift; - - $self->TraversePrePost( - sub { - }, - sub { - my $node = shift; - - return if $node->isRoot or $node->getParent->isRoot; - - # We're only looking for aggregators (AND/OR) - return if ref $node->getNodeValue; - - return if $node->getChildCount != 0; - - # OK, this is a childless aggregator. Remove self. - - $node->getParent->removeChild($node); - - # Deal with circular refs - $node->DESTROY; - } - ); -} - -=head2 GetDisplayedNodes - -This function returns a list of the nodes of the tree in depth-first -order which correspond to options in the "clauses" multi-select box. -In fact, it's all of them but the root and its child. - -=cut - -sub GetDisplayedNodes { - my $self = shift; - my @lines; - - $self->traverse(sub { - my $node = shift; - - push @lines, $node unless $node->isRoot or $node->getParent->isRoot; - }); - - return @lines; -} - - -eval "require RT::Interface::Web::QueryBuilder::Tree_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web/QueryBuilder/Tree_Vendor.pm}); -eval "require RT::Interface::Web::QueryBuilder::Tree_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web/QueryBuilder/Tree_Local.pm}); - -1; diff --git a/rt/lib/RT/Interface/Web/Standalone.pm b/rt/lib/RT/Interface/Web/Standalone.pm deleted file mode 100755 index bc2423e6d..000000000 --- a/rt/lib/RT/Interface/Web/Standalone.pm +++ /dev/null @@ -1,37 +0,0 @@ -package RT::Interface::Web::Standalone; - -use strict; -use base 'HTTP::Server::Simple::Mason'; -use RT::Interface::Web::Handler; -use RT::Interface::Web; - -sub handler_class { "RT::Interface::Web::Handler" } - -sub setup_escapes { - my $self = shift; - my $handler = shift; - - # Override HTTP::Server::Simple::Mason's version of this method to do - # nothing. (RT::Interface::Web::Handler does this already for us in - # NewHandler.) -} - -sub default_mason_config { - return @RT::MasonParameters; -} - -sub handle_request { - - my $self = shift; - my $cgi = shift; - - Module::Refresh->refresh if $RT::DevelMode; - - $self->SUPER::handle_request($cgi); - $RT::Logger->crit($@) if ($@); - - RT::Interface::Web::Handler->CleanupRequest(); - -} - -1; diff --git a/rt/lib/RT/Interface/Web_Vendor.pm b/rt/lib/RT/Interface/Web_Vendor.pm deleted file mode 100644 index 5be20e6b9..000000000 --- a/rt/lib/RT/Interface/Web_Vendor.pm +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am> -# -# 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. - -=head1 NAME - -RT::Interface::Web_Vendor - -=head1 SYNOPSIS - -=head1 DESCRIPTION - -Freeside vendor overlay for RT::Interface::Web. - -=begin testing - -use_ok(RT::Interface::Web_Vendor); - -=end testing - -=cut - -#package RT::Interface::Web; -#use strict; - -package HTML::Mason::Commands; -use strict; - -=head2 ProcessTicketCustomers - -=cut - -sub ProcessTicketCustomers { - my %args = ( - TicketObj => undef, - ARGSRef => undef, - @_ - ); - my @results = (); - - my $Ticket = $args{'TicketObj'}; - my $ARGSRef = $args{'ARGSRef'}; - - ### false laziness w/RT::Interface::Web::ProcessTicketLinks - # Delete links that are gone gone gone. - foreach my $arg ( keys %$ARGSRef ) { - if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) { - my $base = $1; - my $type = $2; - my $target = $3; - - push @results, - "Trying to delete: Base: $base Target: $target Type $type"; - my ( $val, $msg ) = $Ticket->DeleteLink( Base => $base, - Type => $type, - Target => $target ); - - push @results, $msg; - - } - - } - ### - - my @delete_custnums = - map { /^Ticket-AddCustomer-(\d+)$/; $1 } - grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} } - keys %$ARGSRef; - - my @custnums = map { /^Ticket-AddCustomer-(\d+)$/; $1 } - grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} } - keys %$ARGSRef; - - foreach my $custnum ( @custnums ) { - my( $val, $msg ) = - $Ticket->AddLink( 'Type' => 'MemberOf', - 'Target' => "freeside://freeside/cust_main/$custnum", - ); - push @results, $msg; - } - - return @results; - -} - -1; - |