import rt 2.0.14
[freeside.git] / rt / bin / rt
1 #!!!PERL!! -w
2 #
3 # $Header: /home/cvs/cvsroot/freeside/rt/bin/Attic/rt,v 1.1 2002-08-12 06:17:07 ivan Exp $
4 # RT is (c) 1996-2001 Jesse Vincent <jesse@bestpractical.com>
5
6 use strict;
7 use Carp;
8 use Getopt::Long;
9
10 use lib "!!RT_LIB_PATH!!";
11 use lib "!!RT_ETC_PATH!!";
12
13 use RT::Interface::CLI  qw(CleanEnv LoadConfig DBConnect 
14                            GetCurrentUser GetMessageContent);
15
16 #Clean out all the nasties from the environment
17 CleanEnv();
18
19 #Load etc/config.pm and drop privs
20 LoadConfig();
21
22 #Connect to the database and get RT::SystemUser and RT::Nobody loaded
23 DBConnect();
24
25 #Drop setgid permissions
26 RT::DropSetGIDPermissions();
27
28 #Get the current user all loaded
29 my $CurrentUser = GetCurrentUser();
30
31 unless ($CurrentUser->Id) {
32         print "No RT user found. Please consult your RT administrator.\n";
33         exit(1);
34 }
35
36
37 # {{{ commandline flags 
38
39 my ( @id,
40      @limit_queue,
41      @limit_status,
42      @limit_owner,
43      @limit_priority,
44      @limit_final_priority,
45      @limit_requestor,
46      @limit_subject,
47      @limit_body,
48      @limit_created,
49      @limit_resolved,
50      @limit_lastupdated,
51      @limit_dependson,
52      @limit_dependedonby,
53      @limit_memberof,
54      @limit_hasmember,
55      @limit_refersto,
56      @limit_referredtoby,
57      @limit_keyword,
58      
59      @limit_due,
60      @limit_starts,
61      @limit_started,
62      $limit_first,
63      $limit_rows,
64      $history,
65      $summary,
66      $create,
67      @requestors,
68      @cc,
69      @admincc,
70      $status,
71      $subject,
72      $owner,
73      $steal,
74      $queue,
75      $time_left,
76      $priority,
77      $final_priority,
78      $due,
79      $starts,
80      $started,
81      $contacted,
82      $comment,
83      $reply,
84      $source,
85      $edit,
86      @dependson,
87      @memberof, 
88      @refersto,
89      $mergeinto,
90      @keywords,
91      $time_taken,
92      $verbose,
93      $debug,
94    $help,
95    $version);
96
97 # }}}
98
99 # Set defaults for cli args
100
101 $edit = 1; # Assume the user wants to edit replies and comments 
102            # unless they specify --noedit
103
104 # {{{    args
105
106 my @args =("id=s" => \@id,
107            "limit-queue=s" => \@limit_queue,
108            "limit-status=s" => \@limit_status,
109            "limit-owner=s" => \@limit_owner,
110            "limit-priority=s" => \@limit_priority,
111            "limit-final-priority=s" => \@limit_final_priority,
112            "limit-requestor=s" => \@limit_requestor,
113            "limit-subject=s" => \@limit_subject,
114            "limit-body=s",      \@limit_body,
115            "limit-created=s" => \@limit_created,
116            "limit-due=s" =>     \@limit_due,
117            "limit-last-updated=s" => \@limit_lastupdated,
118            "limit-keyword=s" => \@limit_keyword,
119
120            "limit-member-of=s" => \@limit_memberof,
121            "limit-has-member=s" => \@limit_hasmember,
122            "limit-depended-on-by=s" => \@limit_dependedonby,
123            "limit-depends-on=s" => \@limit_dependson,
124            "limit-referred-to-by=s" => \@limit_referredtoby,
125            "limit-refers-to=s" => \@limit_refersto,
126
127            "limit-starts=s" => \@limit_starts,
128            "limit-started=s" => \@limit_started,
129            "limit-first=i" => \$limit_first,
130            "limit-rows=i" => \$limit_rows,
131            "history|show" => \$history,
132            "summary:s" => \$summary,
133            "create" => \$create,
134            "keywords=s" => \@keywords,
135            "requestor|requestors=s" => \@requestors,
136            "cc=s" => \@cc,
137            "admincc=s" => \@admincc,
138            "status=s" => \$status,
139            "subject=s" => \$subject,
140            "owner=s" => \$owner,
141            "steal" => \$steal,
142            "queue=s" => \$queue,
143
144            
145            "priority=i" => \$priority,
146            "final-priority=i" => \$final_priority,
147            "due=s" => \$due,
148            "starts=s" => \$starts,
149            "started=s" => \$started,
150            "contacted=s" => \$contacted,
151            "comment", \$comment,
152            "reply|respond", \$reply,
153            "source=s" => \$source,
154            "edit!" => \$edit,
155            "depends-on=s" => \@dependson,
156            "member-of=s" => \@memberof, 
157            "merge-into=s" => \$mergeinto, 
158            "refers-to=s" => \@refersto,
159            "time-left=i" => \$time_left,
160            "time-taken=i" => \$time_taken,
161            "verbose+" => \$verbose,
162            "debug" => \$debug,
163            "version" => \$version,
164            "help|h|usage" => \$help
165           );
166
167 # }}}
168
169
170
171 GetOptions(@args);
172
173 print join(':',@keywords);
174 # {{{ If they want it, print a usage message and get out
175
176 if ($help) {
177
178
179 print <<EOUSAGE;
180
181 Limit the set of records returned:
182
183 --id=[first][-][last]
184   Specify a single ticket, a range, or to start with (n-) or end with (-n)
185 a specific ticket.
186   
187   --limit-queue=<queue>
188           --limit-status=[!](new|open|stalled|resolved)
189
190           --limit-owner=[!]<userid>
191           --limit-priority=[starts][-][ends]
192           --limit-final-priority=[starts][-][ends]
193             starts is less than ends
194           --limit-requestor=[!]<userid>|<email>
195           --limit-subject=[!]<text>
196           --limit-body=[!]<text>
197           --limit-keyword=[!]<select>/<keyword>
198         
199        Links
200           --limit-member-of=<ticketid>
201           --limit-has-member=<ticketid>
202           --limit-refers-to=<ticketid>
203           --limit-referred-to-by=<ticketid>
204           --limit-depends-on=<ticketid>
205           --limit-depended-on-by=<ticketid>
206
207
208        Dates
209           --limit-created=[starts][-][ends]
210           --limit-due=[starts][-][ends]
211           --limit-starts=[starts][-][ends]
212           --limit-started=[starts][-][ends]
213           --limit-resolved=[starts][-][ends]
214           --limit-last-updated=[starts][-][ends]
215             starts and ends are dates.  starts can not be less than ends
216
217           --limit-first=<first row returned>
218           --limit-rows=<row count>
219
220           --history | --show
221             show a history of the tickets found
222  
223
224           --summary [format-string]
225              show a listing-style summary of the tickets found. If format string
226              is ommitted, uses \$RT_SUMMARY_FORMAT or an internal default
227             
228
229              #TODO: doc summary 
230              format: <atom>%<format>
231              atom:   <name><size>
232              size: <integer>
233              name:  (grep for # {{{ attribs for the array of ok values)
234
235
236           --create
237             create a new ticket. Any attributes that you can modify on an existing ticket
238             can also be used for ticket creation.
239
240
241
242 Attributes
243   Basics
244           --status=<new|open|stalled|resolved|dead>
245            sets status
246            --subject=<subject>
247            sets subject
248            --owner=<userid>
249            set owner to 
250            --steal
251            Become the owner, even if someone else owns the ticket
252            --queue=<queueid>
253            set queue to
254            
255            --priority=<int>
256           
257            --final-priority=<int>
258
259   Watchers
260           --requestors=[+|-]<userid|email address>
261           add or remove this user as a ticket requestor 
262           --cc=[+|-]<userid|email address>
263           add or remove this user as a ticket cc
264           --admincc=[+|-]<userid|email address>
265           add or remove this user as a ticket admincc
266
267         (When creating tickets, just leave off the + or - )
268
269   Keywords
270           --keywords[+|-]<keyword_select>/<keyword>
271           Add or remove a keyword.
272
273
274
275   Dates
276            --due=<date>
277            --starts=<date>
278            --started=<date>
279            --contacted=<date>
280
281            --time-left=<int>
282              
283            --time-taken=<int>
284
285
286    Link related manipulation:
287
288            --depends-on=[+|-]<ticketid>
289            --member-of=[+|-]<ticketid>
290            --refers-to=[+|-]<ticketid>
291            --merge-into=<ticketid>
292
293 Comments and replies
294
295            --comment
296            --reply|respond
297              --source <path>
298                 Specify the path to the source file for this ticket update
299
300               --noedit
301                 Don't invoke \$EDITOR to edit the content of this update
302
303
304
305
306    Condiments
307
308            --verbose
309            --debug
310            --version
311            --help|h|usage
312              You're reading it.
313
314 EOUSAGE
315
316     exit(0);
317 }
318
319 # Print version, and leave
320 if ($version) {
321         print "RT $RT::VERSION for $RT::rtname. Copyright 1996-2001 Jesse Vincent <jesse\@fsck.com>\n";
322         exit(0);
323 }
324
325 # }}}
326
327 # {{{ Validate any options that were passed in. normalize them.
328
329 #if a queue was specified
330 if ($queue) {
331     # make sure that $queue is a valid queue and load it into $queue_obj
332 }
333
334 #For each date in: $due, $starts, $started
335
336 # load up an RT::Date object and parse it into a normalized form
337 # if it can't parse it, log an error and null out the variable
338
339 # }}}
340
341 # {{{ Check if we're creating, if so, create the ticket and be done
342
343 if ($create) {
344     $RT::Logger->debug("Creating a new ticket");
345
346     #Make sure the current user can create tickets in this queue
347     
348     #Make sure that the owner specified can own tickets in this queue
349
350
351             
352     my $linesref = GetMessageContent( Edit => $edit, Source => $source,
353                                       CurrentUser => $CurrentUser
354                                     );
355     
356     require MIME::Entity;
357     my $MIMEObj;
358     
359     if ($linesref) {
360         $MIMEObj = MIME::Entity->build(Data => $linesref);
361     }   
362     
363     use RT::Ticket;
364     my $Ticket=new RT::Ticket($CurrentUser);
365     my ($ticket, $trans, $msg) =
366       $Ticket->Create(Queue => $queue,
367                       Owner => $owner,
368                       Status => $status || 'new' ,
369                       Subject => $subject,
370                       Requestor => \@requestors,
371                       Cc => \@cc,
372                       AdminCc => \@admincc,
373                       Due => $due,
374                       Starts => $starts,
375                       Started => $started,
376                       TimeLeft => $time_left,
377                       InitialPriority => $priority,
378                       FinalPriority => $final_priority,
379                       MIMEObj => $MIMEObj
380                      );
381     print $msg . "\n";
382 }
383
384 # }}}
385
386 else {
387     #Apply restrictions
388     use RT::Tickets;
389     my $Tickets = new RT::Tickets($CurrentUser);
390     
391     # {{{ Limit our search
392     my $value;                  #to use when iterating through restrictions
393     my $queue_id;               #to use when limiting by keyword
394     
395     # {{{ limit on id
396
397     foreach $value (@id) {
398         if ($value =~ /^(\d+)$/) {
399             $Tickets->LimitId ( VALUE => $1,
400                                 OPERATOR => '=');
401         }       
402         elsif ($value =~ /^(\d*)\D?(\d*)$/) {
403             my $start = $1;
404             my $end = $2;
405             $Tickets->LimitId(
406                               VALUE => "$start",
407                               OPERATOR => '>=') if ($start);
408             $Tickets->LimitId(
409                               VALUE => "$end",
410                               OPERATOR => '<=') if ($end);
411         }       
412     }
413
414
415     # }}}
416     
417     # {{{ limit on status
418
419     foreach $value (@limit_status) {
420         if ($value =~ /^(=|!=|!|)(.*)$/) {
421             my $op = $1;
422             my $val = $2;
423                  
424
425             $op = ParseBooleanOp($op);
426             $Tickets->LimitStatus(VALUE => "$val",
427                                   OPERATOR => "$op");
428         }       
429     }
430
431     # }}}
432
433
434
435     # {{{ limit on queue
436     foreach $value (@limit_queue) {
437         if ($value =~ /^(\W?)(.*?)$/i) {
438             my $op = $1;
439             my $val = $2;
440                 
441             $op = ParseBooleanOp($op);
442
443             my $queue_obj = new RT::Queue($RT::SystemUser);
444                 
445             unless ($queue_obj->Load($val)) {
446                 $RT::Logger->debug("Queue '$val' not found");
447                 print STDERR "Queue '$val' not found\n";        
448                 exit(-1);
449             }
450             $RT::Logger->debug ("Limiting queue to $op ".$queue_obj->Name);
451             $Tickets->LimitQueue(VALUE => $queue_obj->Name,
452                                  OPERATOR => $op);
453             $queue_id=$queue_obj->id;
454         }       
455     }   
456
457     # {{{ limit on keyword
458     foreach $value (@limit_keyword) {
459         if ($value =~ /^(\W?)(.*?)\/(.*)$/i) {
460             my $op = $1;
461             my $select = $2;
462             my $keyword = $3;
463
464             $op = ParseBooleanOp($op);
465
466             # load the keyword select
467             my $keyselect = RT::KeywordSelect->new($RT::SystemUser);
468             unless ($keyselect->LoadByName(Name=>$select, Queue=>$queue_id)) {
469                 $RT::Logger->debug("KeywordSelect '$select' not found");
470                 print STDERR "KeywordSelect '$select' not fount\n";
471                 exit(-1);
472             }
473
474             # load the keyword
475             my $k = RT::Keyword->new($RT::SystemUser);
476             unless ($k->LoadByNameAndParentId($keyword, $keyselect->Keyword)) {
477                 $RT::Logger->debug("Keyword '$keyword' not found");
478                 print STDERR "Keyword '$keyword' not found\n";
479                 exit(-1);
480             }
481             $Tickets->LimitKeyword(OPERATOR => $op,
482                                    KEYWORDSELECT => $keyselect->id,
483                                    KEYWORD => $k->id);
484             $RT::Logger->debug ("Limiting keyword to $op ".$k->Path);
485         }
486     }
487     # }}}
488     # {{{ limit on owner
489     foreach $value (@limit_owner) {
490         if ($value =~ /^(\W?)(.*?)$/i) {
491             my $op = $1;
492             my $val = $2;
493                 
494             $op = ParseBooleanOp($op);
495
496             my $user_obj = new RT::User($RT::SystemUser);
497                 
498             unless ($user_obj->Load($val)) {
499                 $RT::Logger->debug("User '$val' not found");
500                 print STDERR "User '$val' not found\n"; 
501                 exit(-1);
502             }
503             $val = $user_obj->id();
504                 
505             $RT::Logger->debug ("Limiting owner to $op $val");
506             $Tickets->LimitOwner(VALUE => "$val",
507                                  OPERATOR => "$op");
508         }       
509     }   
510     # }}}
511     # {{{ limt on priority
512
513     foreach $value (@limit_priority) {
514         my ($start, $end) = ParseRange($value);
515         if ($start == $end) {
516             $Tickets->LimitPriority( VALUE => $start,
517                                      OPERATOR => '=');
518         } elsif ($start) {
519             $Tickets->LimitPriority( VALUE => $start,
520                                      OPERATOR => '>=');
521         } elsif ($end) {
522             $Tickets->LimitPriority( VALUE => $end,
523                                      OPERATOR => '<=');
524         }       
525             
526     }
527     foreach $value (@limit_final_priority) {
528         my ($start, $end) = ParseRange($value);
529         if ($start == $end) {
530             $Tickets->LimitFinalPriority( VALUE => $start,
531                                           OPERATOR => '=');
532         } elsif ($start) {
533             $Tickets->LimitFinalPriority( VALUE => $start,
534                                           OPERATOR => '>=');
535         } elsif ($end) {
536             $Tickets->LimitFinalPriority( VALUE => $end,
537                                           OPERATOR => '<=');
538         }       
539     }
540     # }}}
541
542     foreach $value (@limit_requestor) {
543         if ($value =~ /^(\W?)(.*?)$/i) {
544             my $op = $1;
545             my $val = $2;
546                 
547             $op = ParseBooleanOp($op);
548             $Tickets->LimitRequestor(VALUE => $val,
549                                      OPERATOR => $op );
550         }
551             
552     }
553     foreach $value (@limit_subject) {
554         
555         if ($value =~ /^(\W?)(.*?)$/i) {
556             my $op = $1;
557             my $val = $2;
558             
559             $op = ParseLikeOp($op);
560             
561             $Tickets->LimitSubject(VALUE => $val,
562                                    OPERATOR => $op );
563             }
564     }
565     
566     foreach $value (@limit_body) {
567         if ($value =~ /^(\W?)(.*?)$/i) {
568             my $op = $1;
569             my $val = $2;
570             
571             $op = ParseLikeOp($op);
572             
573                 $Tickets->LimitBody(VALUE => $val,
574                                     OPERATOR => $op );
575         }       
576         
577     }
578     
579     
580     
581     # Dates
582     foreach my $date (@limit_created) {
583         my ($start, $end) = ParseDateRange($date);
584         $Tickets->LimitCreated ( VALUE => $start,
585                                  OPERATOR => '>=' ) if ($start);
586         $Tickets->LimitCreated ( VALUE => $end,
587                                  OPERATOR => '<=' ) if ($end);
588     }
589
590     foreach my $date (@limit_due) {
591         my ($start, $end) = ParseDateRange($date);
592         $Tickets->LimitDue ( VALUE => $start,
593                                  OPERATOR => '>=' ) if ($start);
594         $Tickets->LimitDue ( VALUE => $end,
595                                  OPERATOR => '<=' ) if ($end);
596     }
597
598     foreach my $date (@limit_starts) {
599         my ($start, $end) = ParseDateRange($date);
600         $Tickets->LimitStarts ( VALUE => $start,
601                                  OPERATOR => '>=' ) if ($start);
602         $Tickets->LimitStarts ( VALUE => $end,
603                                  OPERATOR => '<=' ) if ($end);
604     }
605
606     foreach my $date (@limit_started) {
607         my ($start, $end) = ParseDateRange($date);
608         $Tickets->LimitStarted ( VALUE => $start,
609                                  OPERATOR => '>=' ) if ($start);
610         $Tickets->LimitStarted ( VALUE => $end,
611                                  OPERATOR => '<=' ) if ($end);
612     }
613
614     foreach my $date (@limit_resolved) {
615         my ($start, $end) = ParseDateRange($date);
616         $Tickets->LimitResolved ( VALUE => $start,
617                                  OPERATOR => '>=' ) if ($start);
618         $Tickets->LimitResolved ( VALUE => $end,
619                                  OPERATOR => '<=' ) if ($end);
620     }
621
622     foreach my $date (@limit_lastupdated) {
623         my ($start, $end) = ParseDateRange($date);
624         $Tickets->LimitLastUpdated( VALUE => $start,
625                                  OPERATOR => '>=' ) if ($start);
626         $Tickets->LimitLastUpdated ( VALUE => $end,
627                                  OPERATOR => '<=' ) if ($end);
628     }
629
630     foreach my $link (@limit_memberof) {
631         $Tickets->LimitMemberOf($link);
632     }   
633
634     foreach my $link (@limit_hasmember) {
635         $Tickets->LimitHasMember($link);
636     }   
637
638     foreach my $link (@limit_dependson) {
639         $Tickets->LimitDependsOn($link);
640     }   
641
642     foreach my $link (@limit_dependedonby) {
643         $Tickets->LimitDependedOnBy($link);
644     }
645     foreach my $link (@limit_refersto) {
646         $Tickets->LimitRefersTo($link);
647     }   
648     
649     foreach my $link (@limit_referredtoby) {
650         $Tickets->LimitReferredToBy($link);
651     }   
652
653     
654     if ($limit_first){
655     }
656     if ($limit_rows){
657     }
658
659 # }}}
660     
661     # {{{ Iterate through all tickets we found
662
663
664     my ($format, $titles, $code);
665     
666     #Set up the summary format if we need to
667     if (defined $summary) {
668         my $format_string = $summary || $ENV{'RT_SUMMARY_FORMAT'} || "%id4%status4%queue7%subject40%requestor16";
669
670         ($format, $titles, $code) = BuildListingFormat($format_string);
671         printf "$format\n", eval "$titles";
672    }    
673
674  
675
676     while (my $Ticket = $Tickets->Next()) {
677         $RT::Logger->debug ("Now working on ticket ". $Ticket->id);
678     
679         #Run through all the ticket modifications we might want to do
680         #TODO: these are all insufficiently lazy and should be replaced with some 
681         # nice foreaches.
682
683
684         # {{{ deal with watchers
685         
686         # add / delete requestors
687         foreach $value (@requestors) {
688             if ($value =~ /^(\W?)(.*)$/) {
689                 my $op = $1;
690                 my $addr = $2;
691                 
692                 $Ticket->AddRequestor(Email => $addr) if ($op eq '+');
693                 $Ticket->DeleteRequestor( $addr) if ($op eq '-');
694             }   
695         }
696         
697         # add / delete ccs
698         foreach $value (@cc) {
699             if ($value =~ /^(\W?)(.*)$/) {
700                 my $op = $1;
701                 my $addr = $2;
702                 $Ticket->AddCc(Email => $addr) if ($op eq '+');
703                 $Ticket->DeleteCc($addr) if ($op eq '-');
704             }   
705         }       
706         
707         # add / delete adminccs
708         $RT::Logger->debug("Looking at admin ccs");
709         foreach $value (@admincc) {
710             if ($value =~ /^(\W?)(.*)$/) {
711                 my $op = $1;
712                 my $addr = $2;
713                 $Ticket->AddAdminCc(Email => $addr) if ($op eq '+');
714                 $Ticket->DeleteAdminCc($addr) if ($op eq '-');
715             }   
716         }       
717
718         # }}}
719         
720         # {{{ Deal with ticket keywords
721
722         my $KeywordSelects = $Ticket->QueueObj->KeywordSelects();
723         $RT::Logger->debug ("Looking at keywords");
724         foreach $value (@keywords) {
725            $RT::Logger->debug("Looking at --keyword=$value");
726             if ($value =~ /^(\W?)(.*?)\/(.*)$/) {
727                 my $op = $1;
728                 my $select = $2;
729                 my $keyword = $3;
730                 
731                 $RT::Logger->debug("Going to $op Keyword $select / $keyword");  
732                 while (my $ks = $KeywordSelects->Next) {
733                     $RT::Logger->debug("$select is select ".$ks->Name." is found");
734                     next unless ($ks->Name =~ /$select/i);
735                     $RT::Logger->debug ("Found a match for $select\n"); 
736                     my $kids = $ks->KeywordObj->Descendents;
737     
738                     my ($kid);
739                     foreach $kid (keys %{$kids}) {
740                         $RT::Logger->debug("Now comparing $keyword with ".$kids->{$kid}. "\n");
741                         next unless ($kids->{$kid} =~ /^$keyword$/i);
742                         $RT::Logger->debug("Going to $op $select / $keyword (".$kids->{$kid} .")");     
743                         $Ticket->DeleteKeyword(KeywordSelect => $ks->id,
744                                             Keyword => $kid) if ($op eq '-');
745                         
746                         $Ticket->AddKeyword(KeywordSelect => $ks->id,
747                                             Keyword => $kid) if ($op eq '+');
748                     }
749                     
750                 }
751             }
752         }
753         # }}}
754         
755         # {{{ deal with links
756
757         # Deal with merging {
758         if ($mergeinto) {
759                 my ($trans, $msg) =$Ticket->MergeInto($mergeinto);
760                 print $msg."\n";
761         }       
762         # add /delete depends-ons
763
764         foreach my $value (@dependson) {
765             if ($value =~ /^(\W?)(.*)$/) {
766                 my $op = $1;
767                 my $ticket = $2;
768                 if (!$op or ($op eq '+')) {
769                     my ($trans, $msg) =
770                       $Ticket->AddLink(Type => 'DependsOn', Target => $ticket);
771                     print $msg."\n";
772                 }
773                 elsif ($op eq '-') {
774                     my ($trans, $msg) = 
775                       $Ticket->DeleteLink(Type => 'DependsOn', Target => $ticket);
776                     print $msg."\n";
777                 }
778
779             }
780         }
781         # add /delete member-of
782         foreach my $value (@memberof) {
783             if ($value =~ /^(\W?)(.*)$/) {
784                 my $op = $1;
785                 my $ticket = $2;
786                 if ($op eq '+') {
787                     my ($trans, $msg) =
788                       $Ticket->AddLink(Type => 'MemberOf', Target => $ticket);
789                     print $msg;
790                 }
791                 elsif ($op eq '-') {
792                     my ($trans, $msg) = 
793                       $Ticket->DeleteLink(Type => 'MemberOf', Target => $ticket);
794                     print $msg;
795                 }
796
797             }
798         }       
799         # add / delete refers-to
800                 foreach my $value (@refersto) {
801             if ($value =~ /^(\W?)(.*)$/) {
802                 my $op = $1;
803                 my $ticket = $2;
804                 if ($op eq '+') {
805                     my ($trans, $msg) =
806                       $Ticket->AddLink(Type => 'RefersTo', Target => $ticket);
807                     print $msg;
808                 }
809                 elsif ($op eq '-') {
810                     my ($trans, $msg) = 
811                       $Ticket->DeleteLink(Type => 'RefersTo', Target => $ticket);
812                     print $msg;
813                 }
814
815             }
816         }
817
818         # }}}
819         
820         # {{{ deal with dates
821         
822         #set due 
823         if ($due) {
824             my $iso = ParseDateToISO($due);
825             if ($iso) {
826                 $RT::Logger->debug("Setting due date to $iso ($due)");
827                 my ($trans, $msg) = 
828                   $Ticket->SetDue($iso);
829                 print $msg;
830             }
831             else {
832                 print "Due date '$due' could not be parsed";
833             }
834         }
835
836         #set starts
837         if ($starts) {
838             my $iso = ParseDateToISO($due);
839             if ($iso) {
840                 my ($trans, $msg) = 
841                   $Ticket->SetStarts($iso);
842                 print $msg."\n";
843             }
844             else {
845                 print "Starts date '$starts' could not be parsed";
846             }
847         }
848         #set started
849                 if ($started) {
850             my $iso = ParseDateToISO($started);
851             if ($iso) {
852                 my ($trans, $msg) = 
853                   $Ticket->SetStarted($iso);
854                 print $msg."\n";
855             }
856             else {
857                 print "Started date '$started' could not be parsed";
858             }
859         }
860         #set contacted
861                 if ($contacted) {
862             my $iso = ParseDateToISO($contacted);
863             if ($iso) {
864                 my ($trans, $msg) = 
865                   $Ticket->SetContacted($iso);
866                 print $msg."\n";
867             }
868             else {
869                 print "Contacted date '$contacted' could not be parsed";
870             }
871         }
872
873     # }}}
874         
875         # {{{ set other attributes
876
877         #Set subject
878         if ($subject) {
879             my ($trans, $msg) = $Ticket->SetSubject($subject);
880             print $msg."\n";
881         }
882         
883         #Set priority
884         if ($priority) {
885             my ($trans, $msg) = 
886               $Ticket->SetPriority($priority);
887             print $msg."\n";
888         }
889         
890         #Set final priority
891         if ($final_priority) {
892             my ($trans, $msg) =
893               $Ticket->SetFinalPriority($final_priority);
894             print $msg."\n";
895         }
896
897         #Set status
898         if ($status) {
899             my ($trans, $msg) = 
900               $Ticket->SetStatus($status);
901             print $msg."\n";
902         }
903         
904         #Set time left
905         if ($time_left) {
906             my ($trans, $msg) = 
907               $Ticket->SetTimeLeft($time_left);
908             print $msg."\n";
909         }
910
911         #Set time_taken 
912         if ($time_taken) {
913             my ($trans, $msg) = 
914               $Ticket->SetTimeTaken($time_taken);
915             print $msg."\n";
916         }
917         
918         #Set owner
919         if ($owner) {
920             my ($trans, $msg) =
921               $Ticket->SetOwner($owner);
922             print $msg."\n";
923         }
924
925         # Steal
926         if ($steal) {
927                 my ($trans, $msg) =
928                  $Ticket->Steal();
929                  print $msg . "\n";
930         }
931         #Set queue 
932         if ($queue) {
933             my ($trans, $msg) = 
934               $Ticket->SetQueue($queue);
935             print $msg."\n";
936         }
937
938     # }}}
939         
940
941
942         # {{{ Perform ticket comments/replies
943         if ($reply) {
944             $RT::Logger->debug("Replying to ticket ".$Ticket->Id);
945             
946             my $linesref = GetMessageContent( Edit => $edit, Source => $source,
947                                              CurrentUser => $CurrentUser
948                                            );
949             
950             #TODO build this entity
951             require MIME::Entity;
952             my $MIMEObj = MIME::Entity->build(Data => $linesref);
953             
954             $Ticket->Correspond( MIMEObj => $MIMEObj ,
955                                  TimeTaken => $time_taken);
956         }       
957         
958         elsif ($comment) {
959             $RT::Logger->debug("Commenting on ticket ".$Ticket->Id);
960         
961             my $linesref =GetMessageContent(Edit => $edit, Source => $source,
962                                             CurrentUser => $CurrentUser);
963             #TODO build this entity
964             require MIME::Entity;
965             my $MIMEObj = MIME::Entity->build(Data => $linesref);
966             
967             $Ticket->Comment( MIMEObj => $MIMEObj,
968                               TimeTaken => $time_taken);
969         }
970
971     # }}}
972         
973         # {{{ Display whatever we need to display
974
975         # {{{ Display a full ticket listing and history
976         if ($history) {
977             #Display the history
978             $RT::Logger->debug("Show history for ".$Ticket->id);
979             
980             if ($Ticket->CurrentUserHasRight("ShowTicket")) {
981                 &ShowSummary($Ticket);
982                 print "\n";
983                 &ShowHistory($Ticket);
984             }
985             else {
986                 print "You don't have permission to view that ticket.\n";
987             }
988         }       
989
990         # }}}
991         
992         # {{{ Display a summary if we need to
993         if (defined $summary) {
994             $RT::Logger->debug ("Show ticket summary with format $format");
995             
996             printf $format."\n", eval $code;
997             
998         }       
999         # }}}
1000
1001         # }}}
1002         
1003     }
1004
1005     # }}}
1006     
1007 }
1008
1009
1010 $RT::Handle->Disconnect();
1011
1012
1013
1014
1015
1016
1017
1018 # {{{ sub ParseBooleanOp
1019
1020 =head2 ParseBooleanOp
1021
1022   Takes an option modifier. returns the apropriate SQL operator.
1023   If it's handed ! or -, returns !=.  Otherwise returns =.
1024
1025 =cut
1026
1027 sub ParseBooleanOp {
1028     
1029     my $op = shift;
1030     
1031     #so that !new limits to not new, etc
1032     if ($op =~ /^(\!|-)/) {
1033         $op = "!=";
1034     }
1035     else {
1036         $op = "=";
1037     }
1038     
1039     return($op);
1040 }
1041
1042 # }}}
1043
1044 # {{{ sub ParseLikeOp
1045 =head2 ParseLikeOp
1046
1047   Takes an option modifier. returns the apropriate SQL operator.
1048   If it's handed ! or -, returns NOT  LIKE.  Otherwise returns LIKE
1049
1050 =cut
1051
1052 sub ParseLikeOp {
1053     
1054     my $op = shift;
1055     
1056     #so that !new limits to not new, etc
1057     if ($op =~ /^(\!|-)/) {
1058         $op = "NOT LIKE";
1059     }
1060     else {
1061         $op = "LIKE";
1062     }
1063     
1064     return($op);
1065 }
1066 # }}}
1067
1068 # {{{ sub ParseDateToISO
1069
1070 =head2 ParseDateToISO
1071
1072 Takes a date in an arbitrary format.
1073 Returns an ISO date and time in GMT
1074
1075 =cut
1076
1077 sub ParseDateToISO {
1078     my $date = shift;
1079
1080         my $date_obj = new RT::Date($CurrentUser);
1081         $date_obj->Set( Format => 'unknown',
1082                         Value => $date
1083                       );
1084         return ($date_obj->ISO);
1085 }
1086
1087 # }}}
1088
1089 # {{{ sub ParseDateRange
1090
1091 =head2 ParseDateRange [RANGE]
1092
1093 Takes a range of dates of the form [<date>][-][<date>] and returns 
1094 starting and ending dates (as ISOs) If a date is specified as neither a starting nor ending 
1095 date, we parse it it as "midnight tonight to midnight tomorrow"
1096
1097 =cut
1098
1099 sub ParseDateRange {
1100     my $in = shift;
1101     my ($start, $end);
1102     
1103     
1104     use RT::Date;
1105     my $start_obj = new RT::Date($CurrentUser);
1106     my $end_obj = new RT::Date($CurrentUser);
1107     
1108     if ($in =~ /^(.*?)-(.*?)$/) {
1109         $start = $1;
1110         $end = $2;
1111
1112         if ($start) {
1113             $start_obj->Set(Format => 'unknown', 
1114                             Value => $start);
1115         }
1116         if ($end) {
1117             $end_obj->Set(Format => 'unknown', 
1118                           Value => $end);
1119         }
1120     }
1121     else {
1122         $start = $in;
1123         $end = $in;
1124
1125         $start_obj->Set(Format => 'unknown', 
1126                         Value => $start);
1127         
1128         $end_obj->Set(Format => 'unknown', 
1129                       Value => $end);
1130         
1131         $start_obj->SetToMidnight();
1132         $end_obj->SetToMidnight();
1133         $end_obj->AddDay();
1134     }   
1135     
1136     if ($start) {
1137         $start = $start_obj->ISO;
1138     }
1139     if ($end) {
1140         $end = $end_obj->ISO;
1141     }
1142
1143     return ($start, $end);
1144 }
1145
1146 # }}}
1147
1148 # {{{ ParseRange
1149 =head2 ParseRange [RANGE]
1150
1151 Takes a range of the form [<int>][-][<int>] and returns 
1152 a first and a last value. If the - is omitted, both $start and $end are the same.
1153 =cut
1154
1155 sub ParseRange {
1156     my $in = shift;
1157     my ($start, $end);
1158     
1159     if ($in =~ /(.*?)-(.*?)/) {
1160         $start = $1;
1161         $end = $2;
1162     }
1163     else {
1164         $start = $in;
1165         $end = $in;
1166     }   
1167     
1168     return ($start, $end);
1169     
1170
1171     
1172 }
1173
1174 # }}}
1175           
1176 # {{{ sub ShowSummary 
1177
1178 sub ShowSummary  {
1179     my $Ticket = shift;
1180
1181
1182     print <<EOFORM;
1183 Serial Number: @{[$Ticket->Id]}   Status:@{[$Ticket->Status]} Worked: @{[$Ticket->TimeWorked]} minutes  Queue:@{[$Ticket->QueueObj->Name]}
1184       Subject: @{[$Ticket->Subject]}
1185    Requestors: @{[$Ticket->RequestorsAsString]}
1186            Cc: @{[$Ticket->CcAsString]}
1187      Admin Cc: @{[$Ticket->AdminCcAsString]}
1188         Owner: @{[$Ticket->OwnerObj->Name]}
1189      Priority: @{[$Ticket->Priority]} / @{[$Ticket->FinalPriority]}
1190           Due: @{[$Ticket->DueAsString]}
1191       Created: @{[$Ticket->CreatedAsString]} (@{[$Ticket->AgeAsString]})
1192  Last Contact: @{[$Ticket->ToldAsString]} (@{[$Ticket->LongSinceToldAsString]})
1193   Last Update: @{[$Ticket->LastUpdatedAsString]} by @{[$Ticket->LastUpdatedByObj->Name]}
1194                  
1195 EOFORM
1196
1197 my $selects = $Ticket->QueueObj->KeywordSelects();
1198     #get the keyword selects
1199     print "Keywords:\n";
1200     while (my $select = $selects->Next) {
1201         print "\t" .$select->Name .": ";
1202         my $keys = $Ticket->KeywordsObj($select->id);   
1203         while (my $key = $keys->Next) {
1204             print $key->KeywordObj->RelativePath($select->KeywordObj) . "  ";
1205             
1206         }       
1207         print "\n";
1208     }
1209     
1210 #iterate through the keyword selects.
1211 #print the keyword select and all the related keywords
1212
1213
1214
1215 #TODO: finish link  descriptions
1216 print "Dependencies: \n";
1217    while (my $l=$Ticket->DependedOnBy->Next) {
1218        print $l->BaseObj->id," (",$l->BaseObj->Subject,") ",$l->Type," this ticket\n";
1219    }
1220    while (my $l=$Ticket->DependsOn->Next) {
1221        print "This ticket ",$l->Type," ",$l->TargetObj->Id," (",$l->TargetObj->Subject,")\n";
1222    }
1223 }
1224
1225 # }}}
1226
1227 # {{{ sub ShowHistory 
1228 sub ShowHistory  {
1229     my $Ticket = shift;
1230     my $Transaction;    
1231     my $Transactions = $Ticket->Transactions;
1232
1233     while ($Transaction = $Transactions->Next) {
1234       &ShowTransaction($Transaction);
1235     }   
1236   }
1237 # }}}
1238
1239 # {{{ sub ShowTransaction 
1240 sub ShowTransaction  {
1241   my $transaction = shift;
1242   
1243 print <<EOFORM;
1244 ==========================================================================
1245 Date: @{[$transaction->CreatedAsString]} (@{[$transaction->TimeTaken]} minutes)
1246 @{[$transaction->Description]}
1247 EOFORM
1248     ;
1249   my $attachments=$transaction->Attachments();
1250   while (my $message=$attachments->Next) {
1251     print <<EOFORM;
1252 --------------------------------------------------------------------------
1253 @{[$message->Headers]}
1254 EOFORM
1255
1256     if ($message->ContentType =~ m{^(text/plain|message|text$)}) {
1257         print $message->Content;
1258     } else {
1259         print $message->ContentType, " not shown";
1260     }
1261   }
1262   print "\n";
1263   return();
1264 }
1265 # }}}
1266
1267
1268 # {{{ sub BuildListingFormat
1269
1270 sub BuildListingFormat {
1271     my $format_string = shift;
1272
1273     my ($id, @format, @code, @titles);
1274     my ($field,$titles,$length, $format);
1275
1276     my $code = "";
1277
1278     # {{{ attribs
1279     my $attribs = { id => { chars => '4',
1280                             justify => 'r',
1281                             title => 'id',
1282                             value => '$Ticket->id',
1283                           },
1284                     
1285                     queue => { chars => '8',
1286                                justify => 'l',
1287                                title => 'Queue',
1288                                value => '$Ticket->QueueObj->Name' 
1289                              },
1290                     subject => { chars => '30',
1291                                  justify => 'l',
1292                                  title => 'Subject',
1293                                  value => '$Ticket->Subject',
1294                                },
1295                     priority => { chars => '2',
1296                                   justify => 'r',
1297                                   title => 'Pri',
1298                                   value => '$Ticket->Priority',
1299                                 },
1300                     final_priority => {  chars => '2',
1301                                          justify => 'r',
1302                                          title => 'Fin',
1303                                          value => '$Ticket->FinalPriority',
1304                                       },
1305                     time_worked => { chars => '6',
1306                                      justify => 'r',
1307                                      title => 'Worked',
1308                                      value => '$Ticket->TimeWorked',
1309                                    },
1310                     time_left => { chars => '5',
1311                                    justify => 'r',
1312                                    title => 'Left',
1313                                    value => '$Ticket->TimeLeft',
1314                                
1315                                  },
1316                 
1317                     status => {  chars => '6',
1318                                  justify => 'r',
1319                                  title => 'Status',
1320                                  value => '$Ticket->Status',
1321                               },
1322                     owner => {  chars => '10',
1323                                 justify => 'r',
1324                                 title => 'Owner',
1325                                 value => '$Ticket->OwnerObj->Name'
1326                              },
1327                     requestor => {  chars => '10',
1328                                     justify => 'r',
1329                                     title => 'Requestor',
1330                                     value => '$Ticket->RequestorsAsString'
1331                                  },
1332                     created => {  chars => '12',
1333                                   justify => 'r',
1334                                   title => 'Created',
1335                                   value => '$Ticket->CreatedAsString'
1336                                },
1337                     updated => {  chars => '12',
1338                                   justify => 'r',
1339                                   title => 'Updated',
1340                                   value => '$Ticket->LastUpdatedAsString'
1341                                },
1342                     due => {  chars => '12',
1343                               justify => 'r',
1344                               title => 'Due',
1345                               value => '$Ticket->DueAsString'
1346                            },
1347                     told => {  chars => '12',
1348                                justify => 'r',
1349                                title => 'Told',
1350                                value => '$Ticket->ToldAsString'
1351                             },
1352                 
1353                 
1354                 
1355                   };
1356
1357     # }}}
1358     
1359
1360     foreach $field (split ('%',$format_string)) {
1361         
1362         if ($field =~ /^(\D*?)(\d*?)$/) {
1363             $id = $1;
1364             $length = $2;
1365         }
1366         else {  
1367             $RT::Logger->debug ("Error parsing $field\n");
1368         }
1369         if ($length) {
1370             push (@format, "%".$length.".".$length."s ");
1371             
1372             push (@code,  $attribs->{"$id"}->{'value'});
1373                   
1374             push (@titles, "'". $attribs->{"$id"}->{title}. "'");
1375         }
1376         
1377         
1378     }
1379      $code = join (',', @code);
1380      $format = join (" ", @format);
1381      $titles = join (', ', @titles);
1382     
1383   
1384     return ($format, $titles, $code);
1385 }
1386
1387 # }}}
1388
1389
1390
1391 1;