diff options
author | ivan <ivan> | 2004-03-11 02:05:38 +0000 |
---|---|---|
committer | ivan <ivan> | 2004-03-11 02:05:38 +0000 |
commit | 289340780927b5bac2c7604d7317c3063c6dd8cc (patch) | |
tree | c4100ab9857ae00c330213af8a46e66c208580e6 /rt/html | |
parent | 945721f48f74d5cfffef7c7cf3a3d6bc2521f5dd (diff) |
import of rt 3.0.9RT_3_0_9
Diffstat (limited to 'rt/html')
65 files changed, 2394 insertions, 276 deletions
diff --git a/rt/html/Admin/Elements/EditCustomField b/rt/html/Admin/Elements/EditCustomField index a09600ba7..7baed168b 100644 --- a/rt/html/Admin/Elements/EditCustomField +++ b/rt/html/Admin/Elements/EditCustomField @@ -24,7 +24,7 @@ <& /Elements/ListActions, actions => \@results &> -<FORM METHOD=GET ACTION="CustomField.html"> +<FORM METHOD=POST ACTION="CustomField.html"> <INPUT TYPE=HIDDEN NAME="CustomField" VALUE="<%$id %>"> <INPUT TYPE=HIDDEN name="Queue" value="<%$Queue%>"> diff --git a/rt/html/Admin/Elements/EditCustomFieldValues b/rt/html/Admin/Elements/EditCustomFieldValues index 64564adfb..2c9e6d082 100644 --- a/rt/html/Admin/Elements/EditCustomFieldValues +++ b/rt/html/Admin/Elements/EditCustomFieldValues @@ -25,7 +25,7 @@ <ul> % while (my $v = $values->Next) { <li> -<font size=-1 color="#336699"><%$v->SortOrder%>:</font> +<INPUT TYPE="text" SIZE="2" NAME="CustomField-<%$CustomField->Id%>-SortOrder<%$v->Id()%>" VALUE="<%$v->SortOrder()%>"> <input type="checkbox" name="CustomField-<%$CustomField->Id%>-DeleteValue" value="<%$v->id%>"> <%$v->Name%> % if ($v->Description) { diff --git a/rt/html/Admin/Elements/EditCustomFields b/rt/html/Admin/Elements/EditCustomFields index a86b051d0..81c984d29 100644 --- a/rt/html/Admin/Elements/EditCustomFields +++ b/rt/html/Admin/Elements/EditCustomFields @@ -26,44 +26,43 @@ <TABLE> <TR> <TD VALIGN=TOP> -<%$caption%>:<BR> +<h2><%$caption%></h2> </TD></TR></TABLE> % if ($CustomFields->Count == 0 ) { <P><i><&|/l&>(No custom fields)</&></i></P> % } else { -<TABLE> - -<TR> -<TD ROWSPAN="<% $CustomFields->Count %>"> -<UL> -% while (my $CustomFieldObj = $CustomFields->Next) { -<LI><A HREF="CustomField.html?Queue=<%$id%>&CustomField=<%$CustomFieldObj->id()%>"><b><%$CustomFieldObj->Name%></b></a> (<% $CustomFieldObj->FriendlyType %>)<br> -<%$CustomFieldObj->Description%> -</LI> -% } -</UL> -</TD> - +<TABLE cellspacing=0 cellpadding=2> % my $count; % while (my $CustomFieldObj = $CustomFields->Next) { +<TR> + <TD valign="TOP"> +% if ($CustomFieldObj->Name) { + <A HREF="CustomField.html?Queue=<%$id%>&CustomField=<%$CustomFieldObj->id()%>"><b><%$CustomFieldObj->Name%></b></a><br> +% } else { + <A HREF="CustomField.html?Queue=<%$id%>&CustomField=<%$CustomFieldObj->id()%>"><i>(<%loc("no name")%>)</i></a><br> +% } + <%$CustomFieldObj->Description%> + </TD> + <TD valign="TOP"> + <i><% $CustomFieldObj->FriendlyType %></i> + </TD> % # show 'move up' unless it's the first item % if ($count++) { -<TR><TD> -<a href="CustomFields.html?id=<%$id%>&CustomField=<%$CustomFieldObj->id%>&Move=-1"><&|/l&>Move up</&></a> + <TD valign="TOP"> + <a href="CustomFields.html?id=<%$id%>&CustomField=<%$CustomFieldObj->id%>&Move=-1"><&|/l&>Move up</&></a> % } else { -<TD ALIGN=RIGHT> + <TD valign="TOP" ALIGN=RIGHT> % } % # show 'move down' unless it's the last item % if (!$CustomFields->IsLast) { % $m->print(' | ') if $count > 1; -<a href="CustomFields.html?id=<%$id%>&CustomField=<%$CustomFieldObj->id%>&Move=1"><&|/l&>Move down</&></a> + <a href="CustomFields.html?id=<%$id%>&CustomField=<%$CustomFieldObj->id%>&Move=1"><&|/l&>Move down</&></a> % } -</TD></TR> -% } - </TD> </TR> +% } + </TABLE> % } <FORM METHOD=GET ACTION="CustomFields.html"> diff --git a/rt/html/Admin/Elements/EditScrip b/rt/html/Admin/Elements/EditScrip index 5393ebfde..1f186c233 100644 --- a/rt/html/Admin/Elements/EditScrip +++ b/rt/html/Admin/Elements/EditScrip @@ -77,6 +77,14 @@ </TR> <TR> <TD ALIGN=RIGHT> +<&|/l&>Stage</&>: +</TD> +<TD> +<& /Admin/Elements/SelectStage, Name => "Scrip-$id-Stage", Default => $scrip->Stage &> +</TD> +</TR> +<TR> +<TD ALIGN=RIGHT> <&|/l&>Template</&>: </TD> <TD> @@ -123,6 +131,7 @@ elsif ($id) { ScripAction ScripCondition Template + Stage Description CustomPrepareCode CustomCommitCode diff --git a/rt/html/Admin/Elements/EditScrips b/rt/html/Admin/Elements/EditScrips index 24515d8c1..07a57f47b 100644 --- a/rt/html/Admin/Elements/EditScrips +++ b/rt/html/Admin/Elements/EditScrips @@ -48,7 +48,9 @@ </TABLE> % } -<& /Elements/Submit &> +<& /Elements/Submit, + Caption => loc("Delete selected scrips"), + Label => loc("Delete") &> </form> <%init> my (@actions); diff --git a/rt/html/Admin/Elements/SelectGroups b/rt/html/Admin/Elements/SelectGroups index 5df49ad04..3cc909b29 100644 --- a/rt/html/Admin/Elements/SelectGroups +++ b/rt/html/Admin/Elements/SelectGroups @@ -29,9 +29,10 @@ <%INIT> my $groups = new RT::Groups($session{'CurrentUser'}); -$groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'System'); +$groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => $Domain); </%INIT> <%ARGS> $Name => 'groups' +$Domain => 'UserDefined'; </%ARGS> diff --git a/rt/html/Admin/Elements/SelectRights b/rt/html/Admin/Elements/SelectRights index 37a06dc4d..8d87ac9a1 100644 --- a/rt/html/Admin/Elements/SelectRights +++ b/rt/html/Admin/Elements/SelectRights @@ -24,7 +24,7 @@ <INPUT TYPE=HIDDEN NAME="CheckACL" VALUE="<%$ACLDesc%>"> <TABLE BORDER=0> <TR> -<TD valign=top width="180"> +<TD valign=top width="180" align="left"> <h3><&|/l&>Current rights</&></h3> % if ($ACLObj->Count() > 0) { <i>(<&|/l&>Check box to revoke right</&>)</i> <BR> @@ -71,6 +71,7 @@ $ACLObj->LimitToObject( $Object); $ACLObj->LimitToPrincipal( Id => $PrincipalId); + $ACLObj->OrderBy(FIELD=>'RightName'); if (ref($Object) && UNIVERSAL::can($Object, 'AvailableRights')) { %Rights = %{$Object->AvailableRights}; diff --git a/rt/html/Admin/Elements/SelectStage b/rt/html/Admin/Elements/SelectStage new file mode 100644 index 000000000..b62964be4 --- /dev/null +++ b/rt/html/Admin/Elements/SelectStage @@ -0,0 +1,39 @@ +%# 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 +<SELECT NAME=<%$Name%>> +% foreach my $stage (qw(TransactionCreate TransactionBatch)) { +<OPTION VALUE=<%$stage%> +<% ($stage eq $Default) && 'SELECTED' %> +><% loc($stage) %> +</OPTION> +% } +<%INIT> +if ($Default eq '') { + $Default = 'TransactionCreate'; +} +</%INIT> +<%ARGS> +$Default => 'TransactionCreate' +$Name => 'Stage' +</%ARGS> diff --git a/rt/html/Admin/Queues/CustomFields.html b/rt/html/Admin/Queues/CustomFields.html index 78c6c2790..ddf39d71a 100644 --- a/rt/html/Admin/Queues/CustomFields.html +++ b/rt/html/Admin/Queues/CustomFields.html @@ -32,7 +32,8 @@ <& /Admin/Elements/EditCustomFields, title => $title, %ARGS &> <%INIT> my $Queue = new RT::Queue($session{'CurrentUser'}); -$Queue->Load($id); +$Queue->Load($id) || Abort(loc("Couldn't load queue", $id)); + my $CustomFields = RT::CustomFields->new($RT::SystemUser); $CustomFields->LimitToQueue($Queue->Id); my $subtabs = { diff --git a/rt/html/Admin/Queues/index.html b/rt/html/Admin/Queues/index.html index f733c25d8..78a1d5d2d 100644 --- a/rt/html/Admin/Queues/index.html +++ b/rt/html/Admin/Queues/index.html @@ -38,7 +38,7 @@ %} </UL> <BR> -<FORM METHOD=POST ACTION="<% $RT::WebPath %>/Admin/Queues/"> +<FORM METHOD=POST ACTION="<% $RT::WebPath %>/Admin/Queues/index.html"> <input type="checkbox" name="FindDisabledQueues"> <&|/l&>Include disabled queues in listing.</&> <div align=right><input type=submit value="<&|/l&>Go!</&>"></div> </FORM> diff --git a/rt/html/Admin/Users/Modify.html b/rt/html/Admin/Users/Modify.html index 370c2e82d..b424ae961 100644 --- a/rt/html/Admin/Users/Modify.html +++ b/rt/html/Admin/Users/Modify.html @@ -224,9 +224,11 @@ else { } else { push @results, loc('User could not be created: [_1]', $msg); } - - } - else { + + # set the id, so the the menu will have the right info + $id = $UserObj->Id; + + } else { $UserObj->Load($id) || $UserObj->Load($Name) || Abort("Couldn't load user '$Name'"); $val = $UserObj->Id(); } diff --git a/rt/html/Admin/Users/index.html b/rt/html/Admin/Users/index.html index a95d4117d..7dc9af6c3 100644 --- a/rt/html/Admin/Users/index.html +++ b/rt/html/Admin/Users/index.html @@ -40,7 +40,7 @@ </UL> <br><br> -<FORM METHOD=POST ACTION="<% $RT::WebPath %>/Admin/Users/"> +<FORM METHOD=POST ACTION="<% $RT::WebPath %>/Admin/Users/index.html"> <&|/l&>Find people whose</&> <& /Elements/SelectUsers &><BR> <input type="checkbox" name="FindDisabledUsers"> <&|/l&>Include disabled users in search.</&> diff --git a/rt/html/Approvals/Display.html b/rt/html/Approvals/Display.html index 921c1e38f..6a265e242 100644 --- a/rt/html/Approvals/Display.html +++ b/rt/html/Approvals/Display.html @@ -26,7 +26,7 @@ <& Elements/Tabs, current_tab => "Approvals/Display.html", Title => $title &> -<form method=post action="<%$RT::WebPath%>/Approvals/"> +<form method=post action="<%$RT::WebPath%>/Approvals/index.html"> <& /Elements/TitleBoxStart, title => $title &> <& /Ticket/Elements/ShowHistory , Ticket => $Ticket, Collapsed => 0, ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0 &> diff --git a/rt/html/Elements/Callback b/rt/html/Elements/Callback index 93ac4c01b..79157e751 100644 --- a/rt/html/Elements/Callback +++ b/rt/html/Elements/Callback @@ -54,10 +54,11 @@ if (!$callbacks) { $cache{$Page,$_CallbackName} = $callbacks; } -foreach my $comp (@$callbacks) { - $m->comp($comp, %ARGS) if $m->comp_exists($comp); +my @rv; +foreach my $comp (sort @$callbacks) { + push @rv, $m->comp($comp, %ARGS) if $m->comp_exists($comp); } -return(1); +return @rv; </%init> <%args> $_CallbackName => 'Default' diff --git a/rt/html/Elements/MessageBox b/rt/html/Elements/MessageBox index 64fdf38b7..32f422206 100644 --- a/rt/html/Elements/MessageBox +++ b/rt/html/Elements/MessageBox @@ -21,7 +21,7 @@ %# %# %# END LICENSE BLOCK -<TEXTAREA COLS=<%$Width%> ROWS=15 WRAP=HARD NAME="<%$Name%>"><& /Elements/Callback, %ARGS &><% $Default %><%$message%><%$signature%></TEXTAREA> +<TEXTAREA COLS=<%$Width%> ROWS=15 WRAP=<%$Wrap%> NAME="<%$Name%>"><& /Elements/Callback, %ARGS &><% $Default %><%$message%><%$IncludeSignature ? $signature : ''%></TEXTAREA> <%INIT> my ($message); @@ -42,6 +42,8 @@ if ($session{'CurrentUser'}->UserObj->Signature) { $QuoteTransaction => undef $Name => 'Content' $Default => '' -$Width => 72 +$Width => $RT::MessageBoxWidth +$Wrap => $RT::MessageBoxWrap +$IncludeSignature => 1 </%ARGS> diff --git a/rt/html/Elements/MyTickets b/rt/html/Elements/MyTickets index 6e2ddc6c3..52dae3b8d 100644 --- a/rt/html/Elements/MyTickets +++ b/rt/html/Elements/MyTickets @@ -69,7 +69,7 @@ <%INIT> -my $rows = 10; +my $rows = $RT::MyTicketsLength; my $MyTickets; $MyTickets = new RT::Tickets ($session{'CurrentUser'}); $MyTickets->LimitOwner(VALUE => $session{'CurrentUser'}->Id); diff --git a/rt/html/Elements/SelectLang b/rt/html/Elements/SelectLang new file mode 100644 index 000000000..cc2c357e0 --- /dev/null +++ b/rt/html/Elements/SelectLang @@ -0,0 +1,56 @@ +%# 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 +<SELECT NAME ="<%$Name%>"> +% if ($ShowNullOption) { +<OPTION VALUE="">-</OPTION> +% } +% foreach my $lang (@lang) { +<OPTION VALUE="<%$lang%>" <%($Default && ($lang eq $Default)) && 'SELECTED'%>><% $lang_to_desc{$lang} %> +% if (($Verbose) and (my $description = I18N::LangTags::List::native_name($lang)) ){ +(<%$description%>) +% } +</OPTION> +% } +</SELECT> +<%ARGS> +$ShowNullOption => 1 +$ShowAllQueues => 1 +$Name => undef +$Verbose => undef +$Default => 0 +$Lite => 0 +</%ARGS> + +<%ONCE> +use I18N::LangTags::List; +my (@lang, %lang_to_desc); +foreach my $lang (map { s/:://; s/_/-/g; $_ } grep { /^\w+::$/ } keys %RT::I18N::) { + next if $lang =~ /i-default|en-us/; + my $desc = I18N::LangTags::List::name($lang); + next unless ($desc); + $desc =~ s/(.*) (.*)/$2 ($1)/; + $lang_to_desc{$lang} = $desc; +} +@lang = sort { $lang_to_desc{$a} cmp $lang_to_desc{$b} } keys %lang_to_desc; +</%ONCE> diff --git a/rt/html/Elements/SelectStatus b/rt/html/Elements/SelectStatus index 2c1ffad39..16a5f2995 100644 --- a/rt/html/Elements/SelectStatus +++ b/rt/html/Elements/SelectStatus @@ -24,6 +24,7 @@ <SELECT NAME ="<%$Name%>"> <OPTION VALUE="">-</OPTION> %foreach my $status (@status) { +%next if ($SkipDeleted && $status eq 'deleted'); <OPTION VALUE="<%$status%>" <%($Default eq $status) && 'SELECTED'%>><%loc($status)%></OPTION> % } </SELECT> @@ -34,4 +35,5 @@ my @status = $queue->StatusArray(); <%ARGS> $Name => undef $Default => undef +$SkipDeleted => 0 </%ARGS> diff --git a/rt/html/Elements/SelectWatcherType b/rt/html/Elements/SelectWatcherType index 26de8f7b9..82aab2a85 100644 --- a/rt/html/Elements/SelectWatcherType +++ b/rt/html/Elements/SelectWatcherType @@ -22,7 +22,9 @@ %# %# END LICENSE BLOCK <SELECT NAME ="<%$Name%>"> +% if ($AllowNull) { <OPTION VALUE="">-</OPTION> +% } %for my $option (@types) { <OPTION VALUE="<%$option%>" <%$option eq $Default && "SELECTED"%>><%loc($option)%></OPTION> %} @@ -38,6 +40,7 @@ else { } </%INIT> <%ARGS> +$AllowNull => 1 $Default=>undef $Scope => 'ticket' $Name => 'WatcherType' diff --git a/rt/html/Elements/SetupSessionCookie b/rt/html/Elements/SetupSessionCookie index 4d728ce70..7a2ad9ff5 100644 --- a/rt/html/Elements/SetupSessionCookie +++ b/rt/html/Elements/SetupSessionCookie @@ -22,18 +22,25 @@ %# %# END LICENSE BLOCK <%init> +return if $m->is_subrequest; # avoid reentrancy, as suggested by masonbook + my %cookies = CGI::Cookie->fetch(); +my $cookiename = "RT_SID_".$RT::rtname.".".$ENV{'SERVER_PORT'}; my %backends = ( mysql => 'Apache::Session::MySQL', Pg => 'Apache::Session::Postgres', - Oracle => 'Apache::Session::Oracle', +# Oracle => 'Apache::Session::Oracle', ) unless $RT::WebSessionClass; my $session_class = $RT::WebSessionClass || $backends{$RT::DatabaseType} || 'Apache::Session::File'; my $pm = "$session_class.pm"; $pm =~ s|::|/|g; require $pm; + # morning bug avoidance attempt -- pdh 20030815 + unless ($RT::Handle->dbh && $RT::Handle->dbh->ping) { + $RT::Handle->Connect(); + } eval { tie %session, $session_class, - $SessionCookie || ( $cookies{'RT_SID'} ? $cookies{'RT_SID'}->value() : undef ), + $SessionCookie || ( $cookies{$cookiename} ? $cookies{$cookiename}->value() : undef ), $backends{$RT::DatabaseType} ? { Handle => $RT::Handle->dbh, LockHandle => $RT::Handle->dbh, @@ -54,22 +61,23 @@ my $pm = "$session_class.pm"; $pm =~ s|::|/|g; require $pm; Directory => $RT::MasonSessionDir, LockDirectory => $RT::MasonSessionDir, }; - undef $cookies{'RT_SID'}; + undef $cookies{$cookiename}; } else { die "RT Couldn't write to session directory '$RT::MasonSessionDir': $@. Check that this dir ectory's permissions are correct."; } } - if ( !$cookies{'RT_SID'} ) { + if ( !$cookies{$cookiename} ) { my $cookie = new CGI::Cookie( - -name => 'RT_SID', + -name => $cookiename, -value => $session{_session_id}, -path => '/', ); $r->header_out('Set-Cookie', $cookie->as_string); } + return(); </%init> <%args> diff --git a/rt/html/Elements/SimpleSearch b/rt/html/Elements/SimpleSearch index 69541f801..4a0d10656 100644 --- a/rt/html/Elements/SimpleSearch +++ b/rt/html/Elements/SimpleSearch @@ -22,6 +22,6 @@ %# %# END LICENSE BLOCK <form action="<% $RT::WebPath %>/index.html"> -<input size="12" name="q" accesskey="0"> +<input size="12" name="q" autocomplete="off" accesskey="0"> <input type="submit" value="<&|/l&>Search</&>"> </form> diff --git a/rt/html/NoAuth/webrt.css b/rt/html/NoAuth/webrt.css index 62c6d66ba..159e79cd6 100644 --- a/rt/html/NoAuth/webrt.css +++ b/rt/html/NoAuth/webrt.css @@ -331,7 +331,9 @@ ul.topnav { margin-bottom:0; } - +<%flags> +inherit => undef +</%flags> <%init> $r->content_type('text/css'); $r->header_out('Expires' ,'+30m'); diff --git a/rt/html/REST/1.0/Forms/queue/default b/rt/html/REST/1.0/Forms/queue/default new file mode 100644 index 000000000..ce5408846 --- /dev/null +++ b/rt/html/REST/1.0/Forms/queue/default @@ -0,0 +1,123 @@ +%# REST/1.0/Forms/queue/default +%# +<%ARGS> +$id +$format => 's' +$changes => {} +</%ARGS> +<%perl> +my @comments; +my ($c, $o, $k, $e) = ("", [], {}, 0); +my %data = %$changes; +my $queue = new RT::Queue $session{CurrentUser}; +my @fields = qw(Name Description CorrespondAddress CommentAddress + InitialPriority FinalPriority DefaultDueIn); +my %fields = map { lc $_ => $_ } @fields; + +if ($id ne 'new') { + $queue->Load($id); + if (!$queue->Id) { + return [ "# Queue $id does not exist.", [], {}, 1 ]; + } +} +else { + if (%data == 0) { + return [ + "# Required: Name", + [ "id", @fields ], + { + id => 'queue/new', + Name => '<queue name>', + Description => "", + CommentAddress => "", + CorrespondAddress => "", + InitialPriority => "", + FinalPriority => "", + DefaultDueIn => "", + }, + 0 + ]; + } + else { + my %v; + my %create = %fields; + + foreach my $k (keys %data) { + if (exists $create{lc $k}) { + $v{$create{lc $k}} = delete $data{$k}; + } + } + + if ($v{Name} eq '<queue name>') { + my %o = keys %$changes; + delete @o{"id", @fields}; + return [ + "# Please set the queue name.", + [ "id", @fields, keys %o ], $changes, 1 + ]; + } + + $queue->Create(%v); + unless ($queue->Id) { + return [ "# Could not create queue.", [], {}, 1 ]; + } + + delete $data{id}; + $id = $queue->Id; + push(@comments, "# Queue $id created."); + goto DONE if %data == 0; + } +} + +if (%data == 0) { + my @data; + + push @data, [ id => "queue/".$queue->Id ]; + foreach my $key (@fields) { + push @data, [ $key => $queue->$key ]; + } + + my %k = map {@$_} @data; + $o = [ map {$_->[0]} @data ]; + $k = \%k; +} +else { + my ($get, $set, $key, $val, $n, $s); + + foreach $key (keys %data) { + $val = $data{$key}; + $key = lc $key; + $n = 1; + + if (exists $fields{$key}) { + $key = $fields{$key}; + $set = "Set$key"; + + next if $val eq $queue->$key; + ($n, $s) = $queue->$set($val); + } + elsif ($key ne 'id') { + $n = 0; + $s = "Unknown field."; + } + + SET: + if ($n == 0) { + $e = 1; + push @comments, "# $key: $s"; + unless (@$o) { + my %o = keys %$changes; + delete @o{"id", @fields}; + @$o = ("id", @fields, keys %o); + $k = $changes; + } + } + } + + push(@comments, "# Queue $id updated.") unless $n == 0; +} + +DONE: +$c ||= join("\n", @comments) if @comments; +return [ $c, $o, $k, $e ]; +</%perl> diff --git a/rt/html/REST/1.0/Forms/queue/ns b/rt/html/REST/1.0/Forms/queue/ns new file mode 100644 index 000000000..360eea86b --- /dev/null +++ b/rt/html/REST/1.0/Forms/queue/ns @@ -0,0 +1,38 @@ +%# 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 +%# REST/1.0/Forms/queue/ns +%# +<%ARGS> +$id +</%ARGS> +<%perl> +use RT::Queues; + +my $queues = new RT::Queues $session{CurrentUser}; +$queues->Limit(FIELD => 'Name', OPERATOR => '=', VALUE => $id); +if ($queues->Count == 0) { + return (0, "No queue named $id exists."); +} +return $queues->Next->Id; +</%perl> diff --git a/rt/html/REST/1.0/Forms/ticket/attachments b/rt/html/REST/1.0/Forms/ticket/attachments new file mode 100644 index 000000000..bcb571a5a --- /dev/null +++ b/rt/html/REST/1.0/Forms/ticket/attachments @@ -0,0 +1,107 @@ +%# 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 +%# REST/1.0/Forms/ticket/attachments +%# +<%ARGS> +$id +$args => undef +</%ARGS> +<%perl> +my @data; +my ($c, $o, $k, $e) = ("", [], {}, ""); +my $ticket = new RT::Ticket $session{CurrentUser}; + +$ticket->Load($id); +unless ($ticket->Id) { + return [ "# Ticket $id does not exist.", [], {}, 1 ]; +} + +my @arglist = split('/', $args); +my ($aid, $content); + +if ($arglist[1] eq 'content') { + $aid = $arglist[0]; + $content = 1; +} else { + $aid = $args; + $content = 0; +} + +if ($aid) { + unless ($aid =~ /^\d+$/) { + return [ "# Invalid attachment id: $aid", [], {}, 1 ]; + } + my $attachment = new RT::Attachment $session{CurrentUser}; + $attachment->Load($aid); + unless ($attachment->Id eq $aid) { + return [ "# Invalid attachment id: $aid", [], {}, 1 ]; + } + if ($content) { + $c = $attachment->Content; + } else { + my @data; + push @data, [ id => $attachment->Id ]; + push @data, [ Subject => $attachment->Subject ]; + push @data, [ Creator => $attachment->Creator ]; + push @data, [ Created => $attachment->Created ]; + push @data, [ Transaction => $attachment->TransactionId ]; + push @data, [ Parent => $attachment->Parent ]; + push @data, [ MessageId => $attachment->MessageId ]; + push @data, [ Filename => $attachment->Filename ]; + push @data, [ ContentType => $attachment->ContentType ]; + push @data, [ ContentEncoding => $attachment->ContentEncoding ]; + push @data, [ Headers => $attachment->Headers ]; + push @data, [ Content => $attachment->Content ]; + + my %k = map {@$_} @data; + $o = [ map {$_->[0]} @data ]; + $k = \%k; + } + +} +else { + my @attachments; + my $transactions = $ticket->Transactions; + while (my $t = $transactions->Next) { + my $attachments = $t->Attachments; + while (my $a = $attachments->Next) { + next unless $a->Filename; + my $size = length($a->Content); + if ($size > 1024) { $size = int($size/102.4)/10 . "k" } + else { $size .= "b" } + push @attachments, $a->Id.": ".$a->Filename." (".$size.")"; + } + } + + if (@attachments) { + $o = [ "id", "Attachments" ]; + $k = { + id => "ticket/".$ticket->Id."/attachments", + Attachments => \@attachments + }; + } +} + +return [ $c, $o, $k, $e ]; +</%perl> diff --git a/rt/html/REST/1.0/Forms/ticket/default b/rt/html/REST/1.0/Forms/ticket/default new file mode 100644 index 000000000..fec499b58 --- /dev/null +++ b/rt/html/REST/1.0/Forms/ticket/default @@ -0,0 +1,253 @@ +%# REST/1.0/Forms/ticket/default +%# +<%ARGS> +$id +$changes => {} +$fields => undef +</%ARGS> +<%perl> +use MIME::Entity; + +my @comments; +my ($c, $o, $k, $e) = ("", [], {}, 0); +my %data = %$changes; +my $ticket = new RT::Ticket $session{CurrentUser}; +my @dates = qw(Created Starts Started Due Resolved Told); +my @people = qw(Requestors Cc AdminCc); +my @create = qw(Queue Requestor Subject Cc AdminCc Owner Status Priority + InitialPriority FinalPriority TimeEstimated TimeWorked + TimeLeft Starts Started Due Resolved); +my @simple = qw(Subject Status Priority Disabled TimeEstimated TimeWorked + TimeLeft InitialPriority FinalPriority); +my %dates = map {lc $_ => $_} @dates; +my %people = map {lc $_ => $_} @people; +my %create = map {lc $_ => $_} @create; +my %simple = map {lc $_ => $_} @simple; + +# Are we dealing with an existing ticket? +if ($id ne 'new') { + $ticket->Load($id); + if (!$ticket->Id) { + return [ "# Ticket $id does not exist.", [], {}, 1 ]; + } + elsif (!$ticket->CurrentUserHasRight('ShowTicket') || + (%data && !$ticket->CurrentUserHasRight('ModifyTicket'))) + { + my $act = %data ? "modify" : "display"; + return [ "# You are not allowed to $act ticket $id.", [], {}, 1 ]; + } +} +else { + if (%data == 0) { + # GET ticket/new: Return a suitable default form. + # We get defaults from queue/1 (XXX: What if it isn't there?). + my $due = new RT::Date $session{CurrentUser}; + my $queue = new RT::Queue $session{CurrentUser}; + my $starts = new RT::Date $session{CurrentUser}; + $queue->Load(1); + $due->SetToNow; + $due->AddDays($queue->DefaultDueIn) if $queue->DefaultDueIn; + $starts->SetToNow; + + return [ + "# Required: Queue, Requestor, Subject", + [ qw(id Queue Requestor Subject Cc AdminCc Owner Status Priority + InitialPriority FinalPriority TimeEstimated Starts Due Text) ], + { + id => "ticket/new", + Queue => $queue->Name, + Requestor => $session{CurrentUser}->Name, + Subject => "", + Cc => [], + AdminCc => [], + Owner => "", + Status => "new", + Priority => $queue->InitialPriority, + InitialPriority => $queue->InitialPriority, + FinalPriority => $queue->FinalPriority, + TimeEstimated => 0, + Starts => $starts->ISO, + Due => $due->ISO, + Text => "", + }, + 0 + ]; + } + else { + # We'll create a new ticket, and fall through to set fields that + # can't be set in the call to Create(). + my (%v, $text); + + foreach my $k (keys %data) { + if (exists $create{lc $k}) { + $v{$create{lc $k}} = delete $data{$k}; + } + elsif (lc $k eq 'text') { + $text = delete $data{$k}; + } + } + + if ($text) { + $v{MIMEObj} = + MIME::Entity->build( + From => $session{CurrentUser}->EmailAddress, + Subject => $v{Subject}, + Data => $text + ); + } + + $ticket->Create(%v); + unless ($ticket->Id) { + return [ "# Could not create ticket.", [], {}, 1 ]; + } + + delete $data{id}; + $id = $ticket->Id; + push(@comments, "# Ticket $id created."); + goto DONE if %data == 0; + } +} + +# Now we know we're dealing with an existing ticket. +if (%data == 0) { + my ($time, $key, $val, @data); + + push @data, [ id => "ticket/".$ticket->Id ]; + push @data, [ Queue => $ticket->QueueObj->Name ] + if (!%$fields || exists $fields->{lc 'Queue'}); + push @data, [ Owner => $ticket->OwnerObj->Name ] + if (!%$fields || exists $fields->{lc 'Owner'}); + push @data, [ Creator => $ticket->CreatorObj->Name ] + if (!%$fields || exists $fields->{lc 'Creator'}); + + foreach (qw(Subject Status Priority InitialPriority FinalPriority)) { + next unless (!%$fields || (exists $fields->{lc $_})); + push @data, [$_ => $ticket->$_ ]; + } + + foreach $key (@people) { + next unless (!%$fields || (exists $fields->{lc $key})); + push @data, [ $key => [ $ticket->$key->MemberEmailAddresses ] ]; + } + + $time = new RT::Date ($session{CurrentUser}); + foreach $key (@dates) { + next unless (!%$fields || (exists $fields->{lc $key})); + $time->Set(Format => 'sql', Value => $ticket->$key); + push @data, [ $key => $time->AsString ]; + } + + $time = new RT::Date ($session{CurrentUser}); + foreach $key (qw(TimeEstimated TimeWorked TimeLeft)) { + next unless (!%$fields || (exists $fields->{lc $key})); + $val = $ticket->$key || 0; + $val = $time->DurationAsString($val*60) if $val; + push @data, [ $key => $val ]; + } + + my %k = map {@$_} @data; + $o = [ map {$_->[0]} @data ]; + $k = \%k; +} +else { + my ($get, $set, $key, $val, $n, $s); + + foreach $key (keys %data) { + $val = $data{$key}; + $key = lc $key; + $n = 1; + + if (ref $val eq 'ARRAY') { + unless ($key =~ /^(?:Requestors|Cc|AdminCc)$/i) { + $n = 0; + $s = "$key may have only one value."; + goto SET; + } + } + + if ($key =~ /^queue$/i) { + next if $val eq $ticket->QueueObj->Name; + ($n, $s) = $ticket->SetQueue($val); + } + elsif ($key =~ /^owner$/i) { + next if $val eq $ticket->OwnerObj->Name; + ($n, $s) = $ticket->SetOwner($val); + } + elsif (exists $simple{$key}) { + $key = $simple{$key}; + $set = "Set$key"; + + next if $val eq $ticket->$key; + ($n, $s) = $ticket->$set($val); + } + elsif (exists $dates{$key}) { + $key = $dates{$key}; + $set = "Set$key"; + + my $time = new RT::Date $session{CurrentUser}; + $time->Set(Format => 'sql', Value => $ticket->$key); + next if ($val =~ /^not set$/i || $val eq $time->AsString); + ($n, $s) = $ticket->$set($val); + } + elsif (exists $people{$key}) { + $key = $people{$key}; + my ($p, @msgs); + + my %new = map {$_=>1} @{ vsplit($val) }; + my %old = map {$_=>1} $ticket->$key->MemberEmailAddresses; + my $type = $key eq 'Requestors' ? 'Requestor' : $key; + + foreach $p (keys %old) { + unless (exists $new{$p}) { + ($s, $n) = $ticket->DeleteWatcher(Type => $type, + Email => $p); + push @msgs, [ $s, $n ]; + } + } + foreach $p (keys %new) { + # XXX: This is a stupid test. + unless ($p =~ /^[\w.+-]+\@([\w.-]+\.)*\w+.?$/) { + $s = 0; + $n = "$p is not a valid email address."; + push @msgs, [ $s, $n ]; + next; + } + unless ($ticket->IsWatcher(Type => $type, Email => $p)) { + ($s, $n) = $ticket->AddWatcher(Type => $type, + Email => $p); + push @msgs, [ $s, $n ]; + } + } + + $n = 1; + if (@msgs = grep {$_->[0] == 0} @msgs) { + $n = 0; + $s = join "\n", map {"# ".$_->[1]} @msgs; + $s =~ s/^# //; + } + } + elsif ($key ne 'id' && $key ne 'type') { + $n = 0; + $s = "Unknown field."; + } + + SET: + if ($n == 0) { + $e = 1; + push @comments, "# $key: $s"; + unless (@$o) { + my %o = keys %$changes; + delete $o{id}; + @$o = ("id", keys %o); + $k = $changes; + } + } + } + push(@comments, "# Ticket ".$ticket->id." updated.") unless $n == 0; +} + +DONE: +$c ||= join("\n", @comments) if @comments; +return [$c, $o, $k, $e]; + +</%perl> diff --git a/rt/html/REST/1.0/Forms/ticket/history b/rt/html/REST/1.0/Forms/ticket/history new file mode 100644 index 000000000..f5c1dc990 --- /dev/null +++ b/rt/html/REST/1.0/Forms/ticket/history @@ -0,0 +1,144 @@ +%# REST/1.0/Forms/ticket/history +%# +<%ARGS> +$id +$args => undef +$format => undef +$fields => undef +</%ARGS> +<%perl> +my $ticket = new RT::Ticket $session{CurrentUser}; +my ($c, $o, $k, $e) = ("", [], {}, ""); + +$ticket->Load($id); +unless ($ticket->Id) { + return [ "# Ticket $id does not exist.", [], {}, 1 ]; +} + +my $trans = $ticket->Transactions(); +my $total = $trans->Count(); + +chomp $args; +my @arglist = split('/', $args); +my ($type, $tid); + +if ($arglist[0] eq 'type') { + $type = $arglist[1]; +} elsif ($arglist[0] eq 'id') { + $tid = $arglist[1]; +} else { + $type = $args; +} + +if ($type) { + # Create, Set, Status, Correspond, Comment, Give, Steal, Take, Told + # CustomField, AddLink, DeleteLink, AddWatcher, DelWatcher + if ($args =~ /^links?$/) { + $trans->Limit(FIELD => 'Type', OPERATOR => 'LIKE', VALUE => '%Link'); + } + elsif ($args =~ /^watchers?$/) { + $trans->Limit(FIELD => 'Type', OPERATOR => 'LIKE', VALUE => '%Watcher'); + } + else { + $trans->Limit(FIELD => 'Type', OPERATOR => '=', VALUE => $type); + } +} elsif ($tid) { + $trans->Limit(FIELD => 'Id', OPERATOR => '=', VALUE => $tid); +} + +if ($tid) { + my @data; + my $t = new RT::Transaction $session{CurrentUser}; + $t->Load($tid); + + push @data, [ id => $t->Id ]; + push @data, [ EffectiveTicket => $t->EffectiveTicket ] + if (!%$fields || exists $fields->{lc 'EffectiveTicket'}); + push @data, [ Ticket => $t->Ticket ] + if (!%$fields || exists $fields->{lc 'Ticket'}); + push @data, [ TimeTaken => $t->TimeTaken ] + if (!%$fields || exists $fields->{lc 'TimeTaken'}); + push @data, [ Type => $t->Type ] + if (!%$fields || exists $fields->{lc 'Type'}); + push @data, [ Field => $t->Field ] + if (!%$fields || exists $fields->{lc 'Field'}); + push @data, [ OldValue => $t->OldValue ] + if (!%$fields || exists $fields->{lc 'OldValue'}); + push @data, [ NewValue => $t->NewValue ] + if (!%$fields || exists $fields->{lc 'NewValue'}); + push @data, [ Data => $t->Data ] + if (!%$fields || exists $fields->{lc 'Data'}); + push @data, [ Description => $t->Description ] + if (!%$fields || exists $fields->{lc 'Description'}); + push @data, [ Content => $t->Content ] + if (!%$fields || exists $fields->{lc 'Content'}); + + + if (!%$fields || exists $fields->{lc 'Content'}) { + my $creator = new RT::User $session{CurrentUser}; + $creator->Load($t->Creator); + push @data, [ Creator => $creator->Name ]; + } + push @data, [ Created => $t->Created ] + if (!%$fields || exists $fields->{lc 'Created'}); + + if (!%$fields || exists $fields->{lc 'Attachments'}) { + my $attachlist; + my $attachments = $t->Attachments; + while (my $a = $attachments->Next) { + my $size = length($a->Content); + if ($size > 1024) { $size = int($size/102.4)/10 . "k" } + else { $size .= "b" } + $attachlist .= "\n" . $a->Id.": ".($a->Filename || "untitled")." (".$size.")"; + } + + push @data, [Attachments => $attachlist]; + } + + my %k = map {@$_} @data; + $o = [ map {$_->[0]} @data ]; + $k = \%k; + +} else { + my (@data, $tids); + $format ||= "s"; + $format = "l" if (%$fields); + + while (my $t = $trans->Next) { + my $tid = $t->Id; + + if ($format eq "l") { + $tids .= "," if $tids; + $tids .= $tid; + } else { + push @$o, $tid; + $k->{$tid} = $t->Description; + } + } + + if ($format eq "l") { + my @tid; + push @tid, "ticket/$id/history/id/$tids"; + my $fieldstring; + foreach my $key (keys %$fields) { + $fieldstring .= "," if $fieldstring; + $fieldstring .= $key; + } + my ($content, $forms); + + $m->subexec("$RT::WebPath/REST/1.0/show", + id => \@tid, + format => $format, + fields => $fieldstring); + return [ $c, $o, $k, $e ]; + } +} + +if (!$c) { + my $sub = $trans->Count(); + $c = "# $sub/$total ($args/total)"; +} + +return [ $c, $o, $k, $e ]; + +</%perl> diff --git a/rt/html/REST/1.0/Forms/ticket/links b/rt/html/REST/1.0/Forms/ticket/links new file mode 100644 index 000000000..8ac9dc29d --- /dev/null +++ b/rt/html/REST/1.0/Forms/ticket/links @@ -0,0 +1,148 @@ +%# 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 +%# REST/1.0/Forms/ticket/links +%# +<%ARGS> +$id +$format => 's' +$changes => undef +</%ARGS> +<%perl> +my @data; +my $ticket = new RT::Ticket $session{CurrentUser}; + +$ticket->Load($id); +if (!$ticket->Id) { + return [ "# Ticket $id does not exist.", [], {}, 1 ]; +} + +my ($c, $o, $k, $e) = ("", [], {}, 0); +my @fields = qw(DependsOn DependedOnBy RefersTo ReferredToBy Members MemberOf); +my %fields = map { lc $_ => $_ } @fields; + +my %lfields = ( + Members => { Type => 'MemberOf', Mode => 'Base' }, + ReferredToBy => { Type => 'RefersTo', Mode => 'Base' }, + DependedOnBy => { Type => 'DependsOn', Mode => 'Base' }, + MemberOf => { Type => 'MemberOf', Mode => 'Target' }, + RefersTo => { Type => 'RefersTo', Mode => 'Target' }, + DependsOn => { Type => 'DependsOn', Mode => 'Target' }, +); + +if ($changes) { + my ($get, $set, $key, $val, $n, $s); + my %data = %$changes; + my @comments; + + foreach $key (keys %data) { + $val = $data{$key}; + $key = lc $key; + $n = 1; + + if (exists $fields{$key}) { + $key = $fields{$key}; + + my %old; + my $field = $lfields{$key}->{Mode}; + while (my $link = $ticket->$key->Next) { + $old{$link->$field} = 1; + } + + my %new; + foreach my $nkey (@{vsplit($val)}) { + if ($nkey =~ /^\d+$/) { + my $uri = new RT::URI $session{CurrentUser}; + my $tick = new RT::Ticket $session{CurrentUser}; + $tick->Load($nkey); + if ($tick->Id) { + $nkey = $uri->FromObject($tick); + } + else { + $n = 0; + $s = "Ticket $nkey does not exist."; + goto SET; + } + } + $new{$nkey} = 1; + } + + foreach my $u (keys %old) { + if (exists $new{$u}) { + delete $new{$u}; + } + else { + my $type = $lfields{$key}->{Type}; + my $mode = $lfields{$key}->{Mode}; + ($n, $s) = $ticket->DeleteLink(Type => $type, $mode => $u); + goto SET; + } + } + foreach my $u (keys %new) { + my $type = $lfields{$key}->{Type}; + my $mode = $lfields{$key}->{Mode}; + ($n, $s) = $ticket->AddLink(Type => $type, $mode => $u); + goto SET; + } + } + elsif ($key ne 'id' && $key ne 'type') { + $n = 0; + $s = "Unknown field: $key"; + } + + SET: + if ($n == 0) { + $e = 1; + push @comments, "# $key: $s"; + unless (@$o) { + @$o = ("id", @fields); + %$k = %data; + } + } + } + + push(@comments, "# Links for ticket $id updated.") unless @comments; + $c = join("\n", @comments) if @comments; +} +else { + my @data; + + push @data, [ id => "ticket/".$ticket->Id."/links" ]; + foreach my $key (@fields) { + my @val; + + my $field = $lfields{$key}->{Mode}; + while (my $link = $ticket->$key->Next) { + push @val, $link->$field; + } + push(@val, "") if (@val == 0 && $format eq 'l'); + push @data, [ $key => [ @val ] ] if @val; + } + + my %k = map {@$_} @data; + $o = [ map {$_->[0]} @data ]; + $k = \%k; +} + +return [ $c, $o, $k, $e ]; +</%perl> diff --git a/rt/html/REST/1.0/Forms/user/default b/rt/html/REST/1.0/Forms/user/default new file mode 100644 index 000000000..6b216e072 --- /dev/null +++ b/rt/html/REST/1.0/Forms/user/default @@ -0,0 +1,141 @@ +%# REST/1.0/Forms/user/default +%# +<%ARGS> +$id +$format => 's' +$changes => {} +</%ARGS> +<%perl> +my @comments; +my ($c, $o, $k, $e) = ("", [], {}, 0); +my %data = %$changes; +my $user = new RT::User $session{CurrentUser}; +my @fields = qw(RealName NickName Gecos Organization Address1 Address2 City + State Zip Country HomePhone WorkPhone MobilePhone PagerPhone + FreeformContactInfo Comments Signature Lang EmailEncoding + WebEncoding ExternalContactInfoId ContactInfoSystem + ExternalAuthId AuthSystem); +my %fields = map { lc $_ => $_ } @fields; + +if ($id ne 'new') { + $user->Load($id); + if (!$user->Id) { + return [ "# User $id does not exist.", [], {}, 1 ]; + } +} +else { + if (%data == 0) { + return [ + "# Required: Name, EmailAddress", + [ qw(id Name EmailAddress Organization Password Comments) ], + { + id => "user/new", + Name => "", + EmailAddress => "", + Organization => "", + Password => "", + Comments => "" + }, + 0 + ]; + } + else { + my %v; + my %create = %fields; + $create{name} = "Name"; + $create{password} = "Password"; + $create{emailaddress} = "EmailAddress"; + $create{contactinfo} = "FreeformContactInfo"; + # Do any fields need to be excluded here? + + foreach my $k (keys %data) { + if (exists $create{lc $k}) { + $v{$create{lc $k}} = delete $data{$k}; + } + } + + $user->Create(%v); + unless ($user->Id) { + return [ "# Could not create user.", [], {}, 1 ]; + } + + $id = $user->Id; + delete $data{id}; + push(@comments, "# User $id created."); + goto DONE if %data == 0; + } +} + +if (%data == 0) { + my @data; + + push @data, [ id => "user/".$user->Id ]; + push @data, [ Name => $user->Name ]; + push @data, [ Password => '********' ]; + push @data, [ EmailAddress => $user->EmailAddress ]; + + foreach my $key (@fields) { + my $val = $user->$key; + + if ($format eq 'l' || (defined $val && $val ne '')) { + $key = "ContactInfo" if $key eq 'FreeformContactInfo'; + push @data, [ $key => $val ]; + } + } + + my %k = map {@$_} @data; + $o = [ map {$_->[0]} @data ]; + $k = \%k; +} +else { + my ($get, $set, $key, $val, $n, $s); + + foreach $key (keys %data) { + $val = $data{$key}; + $key = lc $key; + $n = 1; + + if ($key eq 'name' || $key eq 'emailaddress' || + $key eq 'contactinfo' || exists $fields{$key}) + { + if (exists $fields{$key}) { + $key = $fields{$key}; + } + else { + $key = "FreeformContactInfo" if $key eq 'contactinfo'; + $key = "EmailAddress" if $key eq 'emailaddress'; + $key = "Name" if $key eq 'name'; + } + $set = "Set$key"; + + next if $val eq $user->$key; + ($n, $s) = $user->$set($val); + } + elsif ($key eq 'password') { + ($n, $s) = $user->SetPassword($val) unless $val =~ /^\**$/; + } + elsif ($key ne 'id') { + $n = 0; + $s = "Unknown field."; + } + + SET: + if ($n == 0) { + $e = 1; + push @comments, "# $key: $s"; + unless (@$o) { + my %o = keys %$changes; + delete @o{"id", @fields}; + @$o = ("id", @fields, keys %o); + $k = $changes; + } + } + } + + push(@comments, "# User $id updated.") unless $n == 0; +} + +DONE: +$c ||= join("\n", @comments) if @comments; +return [ $c, $o, $k, $e ]; +</%perl> diff --git a/rt/html/REST/1.0/Forms/user/ns b/rt/html/REST/1.0/Forms/user/ns new file mode 100644 index 000000000..36b323746 --- /dev/null +++ b/rt/html/REST/1.0/Forms/user/ns @@ -0,0 +1,41 @@ +%# 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 +%# REST/1.0/Forms/user/ns +%# +<%ARGS> +$id +</%ARGS> +<%perl> +use RT::Users; + +my $field = "Name"; +$field = "EmailAddress" if $id =~ /\@/; + +my $users = new RT::Users $session{CurrentUser}; +$users->Limit(FIELD => $field, OPERATOR => '=', VALUE => $id); +if ($users->Count == 0) { + return (0, "No user named $id exists."); +} +return $users->Next->Id; +</%perl> diff --git a/rt/html/REST/1.0/NoAuth/mail-gateway b/rt/html/REST/1.0/NoAuth/mail-gateway index 8db80d5ff..359331f58 100644 --- a/rt/html/REST/1.0/NoAuth/mail-gateway +++ b/rt/html/REST/1.0/NoAuth/mail-gateway @@ -29,12 +29,15 @@ $ticket => undef </%ARGS> <%init> use RT::Interface::Email; -my ( $status, $error, $Ticket ) = RT::Interface::Email::Gateway( %ARGS); +my ( $status, $error, $Ticket ) = RT::Interface::Email::Gateway(\%ARGS); </%init> <%flags> inherit => undef # inhibit UTF8 conversion done in /autohandler </%flags> -% if ($status) { +% if ($status == -75 ) { +temporary failure +% } +% elsif ($status == 1) { ok % if ( $Ticket->Id ) { Ticket: <% $Ticket->Id %> diff --git a/rt/html/REST/1.0/autohandler b/rt/html/REST/1.0/autohandler new file mode 100644 index 000000000..9084a1bef --- /dev/null +++ b/rt/html/REST/1.0/autohandler @@ -0,0 +1,32 @@ +%# 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 +%# REST/1.0/autohandler +%# +<%INIT> +use RT::Interface::REST; +$r->content_type('text/plain'); +$m->error_format('text'); +$m->call_next(); +$m->abort(); +</%INIT> diff --git a/rt/html/REST/1.0/dhandler b/rt/html/REST/1.0/dhandler new file mode 100644 index 000000000..ef5217fe0 --- /dev/null +++ b/rt/html/REST/1.0/dhandler @@ -0,0 +1,287 @@ +%# 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 +%# REST/1.0/dhandler +%# +<%ARGS> +@id => () +$fields => undef +$format => undef +$content => undef +</%ARGS> +<%INIT> +use RT::Interface::REST; + +my $output = ""; +my $status = "200 Ok"; +my $object = $m->dhandler_arg; + +my $name = qr{[\w.-]+}; +my $list = '(?:(?:\d+-)?\d+,)*(?:\d+-)?\d+'; +my $label = '[a-zA-Z0-9@_.+-]+'; +my $field = '[a-zA-Z][a-zA-Z0-9_-]*'; +my $labels = "(?:$label,)*$label"; + +# We must handle requests such as the following: +# +# 1. http://.../REST/1.0/show (with a list of object specifications). +# 2. http://.../REST/1.0/edit (with a self-contained list of forms). +# 3. http://.../REST/1.0/ticket/show (implicit type specification). +# http://.../REST/1.0/ticket/edit +# 4. http://.../REST/1.0/ticket/nn (all possibly with a single form). +# http://.../REST/1.0/ticket/nn/history +# http://.../REST/1.0/ticket/nn/attachment/1 +# +# Objects are specified by their type, and either a unique numeric ID, +# or a unique name (e.g. ticket/1, queue/foo). Multiple objects of the +# same type may be specified by a comma-separated list of identifiers +# (e.g., user/ams,rai or ticket/1-3,5-7). +# +# Ultimately, we want a list of object specifications to operate upon. +# The URLs in (4) provide enough information to identify an object. We +# will assemble submitted information into that format in other cases. +# +my (@objects, $forms); +my $utype; + +if ($object eq 'show' || # $REST/show + (($utype) = ($object =~ m{^($name)/show$}))) # $REST/ticket/show +{ + # We'll convert type/range specifications ("ticket/1-3,7-9/history") + # into a list of singular object specifications ("ticket/1/history"). + # If the URL specifies a type, we'll accept only that one. + foreach my $id (@id) { + $id =~ s|^(?:$utype/)?|$utype/| if $utype; + if (my ($type, $oids, $extra) = + ($id =~ m#^($name)/($list|$labels)(?:(/.*))?$#o)) + { + foreach my $oid (expand_list($oids)) { + if ($extra =~ m{^(?:/($name)(?:/(.*))?)?$}o) { + my ($attr, $args) = ($1, $2); + # expand transaction and attachment range specifications + # (if applicable) + my $tids; + if ($attr eq 'history' && $args =~ m#id/(\d.*)#o) { + $tids = $1; + } + if ($tids) { + push(@objects, "$type/$oid/$attr/id/$_") for expand_list($tids); + } else { + push(@objects, "$type/$oid$extra"); + } + } + } + } + else { + $status = "400 Bad Request"; + $output = "Invalid object ID specified: '$id'"; + goto OUTPUT; + } + } +} +elsif ($object eq 'edit' || # $REST/edit + (($utype) = ($object =~ m{^($name)/edit$}))) # $REST/ticket/edit +{ + # We'll make sure each of the submitted forms is syntactically valid + # and sufficiently identifies an object to operate upon, then add to + # the object list as above. + my @output; + + $forms = form_parse($content); + foreach my $form (@$forms) { + my ($c, $o, $k, $e) = @$form; + + if ($e) { + push @output, [ "# Syntax error.", $o, $k, $e ]; + } + else { + my ($type, $id); + + # Look for matching types in the ID, form, and URL. + $type = exists $k->{type} ? $k->{type} : $utype; + $type =~ s|^(?:$utype)?|$utype/| if $utype; + $type =~ s|/$||; + + if (exists $k->{id}) { + $id = $k->{id}; + $id =~ s|^(?:$type/)?|$type/| if $type; + + if ($id =~ m#^$name/(?:$label|\d+)(?:/.*)?#o) { + push @objects, $id; + } + else { + push @output, [ "# Invalid object ID: '$id'", $o, $k, $e ]; + } + } + else { + push @output, [ "# No object ID specified.", $o, $k, $e ]; + } + } + } + # If we saw any errors at this stage, we won't process any part of + # the submitted data. + if (@output) { + unshift @output, [ "# Please resubmit with errors corrected." ]; + $status = "409 Syntax Error"; + $output = form_compose(\@output); + goto OUTPUT; + } +} +else { + # We'll assume that this is in the correct format already. Otherwise + # it will be caught by the loop below. + push @objects, $object; + + if ($content) { + $forms = form_parse($content); + + if (@$forms > 1) { + $status = "400 Bad Request"; + $output = "You may submit only one form to this object."; + goto OUTPUT; + } + + my ($c, $o, $k, $e) = @{ $forms->[0] }; + if ($e) { + $status = "409 Syntax Error"; + $output = form_compose([ ["# Syntax error.", $o, $k, $e] ]); + goto OUTPUT; + } + } +} + +# Make sure we have something to do. +unless (@objects) { + $status = "400 Bad Request"; + $output = "No objects specified."; + goto OUTPUT; +} + +# Parse and validate any field specifications. +my (%fields, @fields); +if ($fields) { + unless ($fields =~ /^(?:$field,)*$field$/) { + $status = "400 Bad Request"; + $output = "Invalid field specification: $fields"; + goto OUTPUT; + } + @fields = map lc, split /,/, $fields; + @fields{@fields} = (); + unless (exists $fields{id}) { + unshift @fields, "id"; + $fields{id} = (); + } +} + +my (@comments, @output); + +foreach $object (@objects) { + my ($handler, $type, $id, $attr, $args); + my ($c, $o, $k, $e) = ("", ["id"], {id => $object}, 0); + + my $i = 0; + if ($object =~ m{^($name)/(\d+|$label)(?:/($name)(?:/(.*))?)?$}o || + $object =~ m{^($name)/(new)$}o) + { + ($type, $id, $attr, $args) = ($1, $2, ($3 || 'default'), $4); + $handler = "Forms/$type/$attr"; + + unless ($m->comp_exists($handler)) { + $args = "$attr/$args"; + $handler = "Forms/$type/default"; + + unless ($m->comp_exists($handler)) { + $i = 2; + $c = "# Unknown object type: $type"; + } + } + elsif ($id ne 'new' && $id !~ /^\d+$/) { + my $ns = "Forms/$type/ns"; + + # Can we resolve named objects? + unless ($m->comp_exists($ns)) { + $i = 3; + $c = "# Objects of type $type must be specified by numeric id."; + } + else { + my ($n, $s) = $m->comp("Forms/$type/ns", id => $id); + if ($n <= 0) { $i = 4; $c = "# $s"; } + else { $i = 0; $id = $n; } + } + } + else { + $i = 0; + } + } + else { + $i = 1; + $c = "# Invalid object specification: '$object'"; + } + + if ($i != 0) { + if ($content) { + (undef, $o, $k, $e) = @{ shift @$forms }; + } + push @output, [ $c, $o, $k ]; + next; + } + + unless ($content) { + my $d = $m->comp($handler, id => $id, args => $args, format => $format, fields => \%fields); + my ($c, $o, $k, $e) = @$d; + + if (!$e && @$o && keys %fields) { + my %lk = map { lc $_ => $_ } keys %$k; + @$o = map { $lk{$_} } @fields; + foreach my $key (keys %$k) { + delete $k->{$key} unless exists $fields{lc $key}; + } + } + push(@output, [ $c, $o, $k ]) if ($c || @$o || keys %$k); + } + else { + my ($c, $o, $k, $e) = @{ shift @$forms }; + my $d = $m->comp($handler, id => $id, args => $args, format => $format, + changes => $k); + ($c, $o, $k, $e) = @$d; + + # We won't pass $e through to compose, trusting instead that the + # handler added suitable comments for the user. + if ($e) { + $status = "409 Syntax Error" if @$o; + push @output, [ $c, $o, $k ]; + } + else { + push @comments, $c; + } + } +} + +unshift(@output, [ join "\n", @comments ]) if @comments; +$output = form_compose(\@output); + +OUTPUT: +</%INIT> +RT/<% $RT::VERSION %> <% $status %> + +<% $output |n %> diff --git a/rt/html/REST/1.0/logout b/rt/html/REST/1.0/logout new file mode 100644 index 000000000..b64938bc2 --- /dev/null +++ b/rt/html/REST/1.0/logout @@ -0,0 +1,27 @@ +%# 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 +<%PERL> +tied(%session)->delete if (defined %session); +</%PERL> +RT/<% $RT::VERSION %> 200 Ok diff --git a/rt/html/REST/1.0/search/dhandler b/rt/html/REST/1.0/search/dhandler new file mode 100644 index 000000000..90b4653e5 --- /dev/null +++ b/rt/html/REST/1.0/search/dhandler @@ -0,0 +1,32 @@ +%# 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 +%# REST/1.0/search/dhandler +%# +<%INIT> +my $status = "500 Server Error"; +my $output = "Unsupported object type."; +</%INIT> +RT/<% $RT::VERSION %> <% $status %> + +<% $output |n %> diff --git a/rt/html/REST/1.0/search/ticket b/rt/html/REST/1.0/search/ticket new file mode 100644 index 000000000..24435294e --- /dev/null +++ b/rt/html/REST/1.0/search/ticket @@ -0,0 +1,119 @@ +%# 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 +%# REST/1.0/search/ticket +%# +<%ARGS> +$query +$format => undef +$orderby => undef +$fields => undef +</%ARGS> +<%INIT> +my $output = ""; +my $status = "200 Ok"; +my $tickets = new RT::Tickets $session{CurrentUser}; + +# Parse and validate any field specifications. +my $field = '[a-zA-Z][a-zA-Z0-9_-]*'; +my (%fields, @fields); +if ($fields) { + $format = "l"; + unless ($fields =~ /^(?:$field,)*$field$/) { + $status = "400 Bad Request"; + $output = "Invalid field specification: $fields"; + goto OUTPUT; + } + @fields = map lc, split /,/, $fields; + @fields{@fields} = (); + unless (exists $fields{id}) { + unshift @fields, "id"; + $fields{id} = (); + } +} + +$format ||= "s"; +if ($format !~ /^[isl]$/) { + $status = "400 Bad request"; + $output = "Unknown listing format: $format. (Use i, s, or l.)\n"; + goto OUTPUT; +} + +my ($n, $s); +eval { + ($n, $s) = $tickets->FromSQL($query); +}; +my $sortstring = ""; +if ($orderby) { + $sortstring = 'FIELD => '; + my $order = substr($orderby, 0, 1); + if ($order eq '+' || $order eq '-') { + $sortstring .= 'substr($orderby, 1)'; + if ($order eq '+') { + $sortstring .= ", ORDER => 'ASC'"; + } elsif ($order eq '-') { + $sortstring .= ", ORDER => 'DESC'"; + } + } else { + $sortstring .= '$orderby'; + } + my $foo = 'FIELD => '; + $foo .= '$orderby'; + $tickets->OrderBy(eval $sortstring); +} +if ($@ || $n == 0) { + $s ||= $@; + $status = "400 Bad request"; + $output = "Invalid query: '$s'.\n"; + goto OUTPUT; +} + +$n = 0; +my @output; +while (my $ticket = $tickets->Next) { + $n++; + + if ($format eq "i") { + $output .= "ticket/" . $ticket->Id . "\n"; + } + elsif ($format eq "s") { + $output .= $ticket->Id . ": ". $ticket->Subject . "\n"; + } + else { + my $id = $ticket->Id; + my $d = $m->comp("$RT::WebPath/REST/1.0/Forms/ticket/default", id => $id, format => $format, fields => \%fields); + my ($c, $o, $k, $e) = @$d; + push @output, [ $c, $o, $k ]; + } +} +if ($n == 0 && $format ne "i") { + $output = "No matching results.\n"; +} + +$output = form_compose(\@output) if @output; + +OUTPUT: +</%INIT> +RT/<% $RT::VERSION %> <% $status %> + +<% $output |n %> diff --git a/rt/html/REST/1.0/ticket/comment b/rt/html/REST/1.0/ticket/comment new file mode 100644 index 000000000..9d1b06246 --- /dev/null +++ b/rt/html/REST/1.0/ticket/comment @@ -0,0 +1,149 @@ +%# 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 +%# REST/1.0/ticket/comment +%# +<%ARGS> +$content +</%ARGS> +<%INIT> +use MIME::Entity; +use LWP::MediaTypes; +use RT::Interface::REST; +use File::Temp qw(tempfile); + +my $ticket = new RT::Ticket $session{CurrentUser}; +my $object = $r->path_info; +my $status = "200 Ok"; +my $output; +my $action; + +# http://.../REST/1.0/ticket/comment/1 +my ($c, $o, $k, $e) = @{ form_parse($content)->[0] }; +if ($e || !$o) { + if (!$o) { + $output = "Empty form submitted.\n"; + } + else { + $c = "# Syntax error."; + $output = form_compose([[$c, $o, $k, $e]]); + } + $status = "400 Bad Request"; + goto OUTPUT; +} + +$object =~ s#^/##; +$object ||= $k->{Ticket}; +unless ($object =~ /^\d+/) { + $output = "Invalid ticket id: `$object'.\n"; + $status = "400 Bad Request"; + goto OUTPUT; +} +if ($k->{Ticket} && $object ne $k->{Ticket}) { + $output = "The submitted form and URL specify different tickets.\n"; + $status = "400 Bad Request"; + goto OUTPUT; +} + +($action = $k->{Action}) =~ s/^(.)(.*)$/\U$1\L$2\E/; +unless ($action =~ /^(?:Comment|Correspond)$/) { + $output = "Invalid action: `$action'.\n"; + $status = "400 Bad Request"; + goto OUTPUT; +} + +my $text = $k->{Text}; +my @atts = @{ vsplit($k->{Attachment}) }; + +if (!$k->{Text} && @atts == 0) { + $status = "400 Bad Request"; + $output = "Empty comment with no attachments submitted.\n"; + goto OUTPUT; +} + +my $cgi = $m->cgi_object; +my $ent = MIME::Entity->build(Type => "multipart/mixed"); +$ent->attach(Data => $k->{Text}) if $k->{Text}; + +my $i = 1; +foreach my $att (@atts) { + local $/=undef; + my $file = $att; + $file =~ s#^.*[\\/]##; + + my $fh = $cgi->upload("attachment_$i"); + if ($fh) { + my $buf; + my ($w, $tmp) = tempfile(); + my $info = $cgi->uploadInfo(); + + while (sysread($fh, $buf, 8192)) { + syswrite($w, $buf); + } + + $ent->attach( + Path => $tmp, + Type => $info->{'Content-Type'} || guess_media_type($tmp), + Filename => $file, + Disposition => "attachment" + ); + } + else { + $status = "400 Bad Request"; + $output = "No attachment for $att.\n"; + goto OUTPUT; + } + + $i++; +} + +$ticket->Load($object); +unless ($ticket->Id) { + $output = "Couldn't load ticket id: `$object'.\n"; + $status = "404 Ticket not found"; + goto OUTPUT; +} +unless ($ticket->CurrentUserHasRight('ModifyTicket') || + ($action eq "Comment" && + $ticket->CurrentUserHasRight("CommentOnTicket")) || + ($action eq "Correspond" && + $ticket->CurrentUserHasRight("ReplyToTicket"))) +{ + $output = "You are not allowed to $action on ticket $object.\n"; + $status = "403 Permission denied"; + goto OUTPUT; +} + +my $cc = join ", ", @{ vsplit($k->{Cc}) }; +my $bcc = join ", ", @{ vsplit($k->{Bcc}) }; +my ($n, $s) = $ticket->$action(MIMEObj => $ent, + CcMessageTo => $cc, + BccMessageTo => $bcc, + TimeTaken => $k->{TimeWorked} || 0); +$output = $s; + +OUTPUT: +</%INIT> +RT/<% $RT::VERSION %> <% $status %> + +<% $output |n %> diff --git a/rt/html/REST/1.0/ticket/link b/rt/html/REST/1.0/ticket/link new file mode 100644 index 000000000..574762512 --- /dev/null +++ b/rt/html/REST/1.0/ticket/link @@ -0,0 +1,96 @@ +%# 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 +%# REST/1.0/ticket/link +%# +<%ARGS> +$id => undef +$del => 0 +$rel +$to +</%ARGS> +<%INIT> +use RT::Interface::REST; + +my $output; +my $status = "200 Ok"; +my $ticket = new RT::Ticket $session{CurrentUser}; +my $object = $r->path_info; + +my @fields = qw(DependsOn DependedOnBy RefersTo ReferredToBy HasMember MemberOf); +my %fields = map { lc $_ => $_ } @fields; +my %lfields = ( + HasMember => { Type => 'MemberOf', Mode => 'Base' }, + ReferredToBy => { Type => 'RefersTo', Mode => 'Base' }, + DependedOnBy => { Type => 'DependsOn', Mode => 'Base' }, + MemberOf => { Type => 'MemberOf', Mode => 'Target' }, + RefersTo => { Type => 'RefersTo', Mode => 'Target' }, + DependsOn => { Type => 'DependsOn', Mode => 'Target' }, +); + +# http://.../REST/1.0/ticket/link/1 + +$object =~ s#^/##; +if ($id && $object && $id != $object) { + $output = "Different ids in URL (`$object') and submitted form (`$id').\n"; + $status = "400 Bad Request"; + goto OUTPUT; +} +$id ||= $object; +unless ($id =~ /^\d+$/ && $to =~ /^\d+$/) { + my $bad = ($id !~ /^\d+$/) ? $id : $to; + $output = $r->path_info. "\n"; + $output .= "Invalid ticket id: '$bad'.\n"; + $status = "400 Bad Request"; + goto OUTPUT; +} +unless (exists $fields{lc $rel}) { + $output = "Invalid relationship: '$rel'.\n"; + $status = "400 Bad Request"; + goto OUTPUT; +} +$rel = $fields{lc $rel}; + +$ticket->Load($id); +unless ($ticket->Id) { + $output = "Couldn't load ticket id: '$id'.\n"; + $status = "404 Ticket not found"; + goto OUTPUT; +} + +my $type = $lfields{$rel}->{Type}; +my $mode = $lfields{$rel}->{Mode}; + +my $n = 1; +my $op = $del ? "DeleteLink" : "AddLink"; + +($n, $output) = $ticket->$op(Type => $type, $mode => $to); +if ($n == 0) { + $status = "500 Error"; +} + +OUTPUT: +</%INIT> +RT/<% $RT::VERSION %> <% $status %> + +<% $output |n %> diff --git a/rt/html/REST/1.0/ticket/merge b/rt/html/REST/1.0/ticket/merge new file mode 100644 index 000000000..9cd2a7cfa --- /dev/null +++ b/rt/html/REST/1.0/ticket/merge @@ -0,0 +1,78 @@ +%# 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 +%# REST/1.0/ticket/merge +%# +<%ARGS> +$id => undef +$into +</%ARGS> +<%INIT> +use RT::Interface::REST; + +my $output; +my $status = "200 Ok"; +my $ticket = new RT::Ticket $session{CurrentUser}; +my $object = $r->path_info; + +# http://.../REST/1.0/ticket/merge/1 + +$object =~ s#^/##; +if ($id && $object && $id != $object) { + $output = "Different ids in URL (`$object') and submitted form (`$id').\n"; + $status = "400 Bad Request"; + goto OUTPUT; +} +$id ||= $object; +unless ($id =~ /^\d+$/ && $into =~ /^\d+$/) { + my $bad = ($id !~ /^\d+$/) ? $id : $into; + $output = $r->path_info. "\n"; + $output .= "Invalid ticket id: `$bad'.\n"; + $status = "400 Bad Request"; + goto OUTPUT; +} + +$ticket->Load($id); +unless ($ticket->Id) { + $output = "Couldn't load ticket id: `$id'.\n"; + $status = "404 Ticket not found"; + goto OUTPUT; +} +unless ($ticket->CurrentUserHasRight('ModifyTicket')) { + $output = "You are not allowed to modify ticket $id.\n"; + $status = "403 Permission denied"; + goto OUTPUT; +} + +my ($n, $s) = $ticket->MergeInto($into); + +if ($n == 0) { + $status = "500 Error"; +} +$output = $s; + +OUTPUT: +</%INIT> +RT/<% $RT::VERSION %> <% $status %> + +<% $output |n %> diff --git a/rt/html/Search/Bulk.html b/rt/html/Search/Bulk.html index df43cfa50..de9143c8a 100644 --- a/rt/html/Search/Bulk.html +++ b/rt/html/Search/Bulk.html @@ -175,6 +175,7 @@ if ($ARGS{'UpdateContent'} && #Iterate through each ticket we've been handed my @linkresults; +$session{'tickets'}->RedoSearch(); while (my $Ticket = $session{'tickets'}->Next) { $RT::Logger->debug( "Checking Ticket ".$Ticket->Id ."\n"); next unless ($ARGS{"UpdateTicket".$Ticket->Id}); diff --git a/rt/html/Search/Elements/PickRestriction b/rt/html/Search/Elements/PickRestriction index a6911df5a..0021ab2bc 100644 --- a/rt/html/Search/Elements/PickRestriction +++ b/rt/html/Search/Elements/PickRestriction @@ -34,9 +34,10 @@ <& /Elements/SelectOwner, Name => "ValueOfOwner" &> <li> -<&|/l&>Requestor email address</&> -<& /Elements/SelectMatch, Name => "RequestorOp" &> -<INPUT Name="ValueOfRequestor" SIZE=20> +<& /Elements/SelectWatcherType, Name => "WatcherRole", AllowNull => 0 &> +<&|/l&>email address</&> +<& /Elements/SelectMatch, Name => "WatcherRoleOp" &> +<INPUT Name="ValueOfWatcherRole" SIZE=20> <li> <&|/l&>Subject</&> <& /Elements/SelectMatch, Name => "SubjectOp" &> @@ -47,7 +48,7 @@ False => loc("isn't"), TrueVal=> '=', FalseVal => '!=' &> -<& /Elements/SelectQueue, Name => loc("ValueOfQueue") &> +<& /Elements/SelectQueue, Name => "ValueOfQueue" &> <li><&|/l&>Priority</&> <& /Elements/SelectEqualityOperator, Name => "PriorityOp" &> @@ -77,7 +78,7 @@ TrueVal=> '=', FalseVal => '!=' &> -<& /Elements/SelectStatus, Name => "ValueOfStatus" &> +<& /Elements/SelectStatus, Name => "ValueOfStatus", SkipDeleted => 1 &> % while ( my $CustomField = $CustomFields->Next ) { diff --git a/rt/html/SelfService/Display.html b/rt/html/SelfService/Display.html index fc3fcb289..124ecf407 100644 --- a/rt/html/SelfService/Display.html +++ b/rt/html/SelfService/Display.html @@ -38,7 +38,7 @@ <& /Elements/TitleBoxStart, title => loc("Dates"), title_class=> 'inverse', color => "#663366" &> - <& /Ticket/Elements/ShowDates, Ticket => $Ticket &> + <& /Ticket/Elements/ShowDates, Ticket => $Ticket, UpdatedLink => 0 &> <& /Elements/TitleBoxEnd &> </TD> </TR> @@ -46,7 +46,7 @@ -<& /Ticket/Elements/ShowHistory, Ticket => $Ticket&> +<& /Ticket/Elements/ShowHistory, Ticket => $Ticket, AttachPath => "Attachment" &> @@ -101,6 +101,12 @@ if ( $id[0] eq 'new' ) { push ( @results, $ErrMsg ); # }}} + +# delete temporary storage entry to make WebUI clean +unless (keys %{$session{'Attachments'}} and $ARGS{'UpdateAttach'}) { + delete $session{'Attachments'}; +} +# }}} } else { unless ( $Ticket->Load( $id[0] ) ) { @@ -127,10 +133,43 @@ if ( ( defined $ARGS{'Status'} ) push @results, "$msg"; } +# {{{ store the uploaded attachment in session +if ($ARGS{'Attach'}) { # attachment? + $session{'Attachments'} = {} unless defined $session{'Attachments'}; + + my $subject = "$ARGS{'Attach'}"; + # since CGI.pm deutf8izes the magic field, we need to add it back. + Encode::_utf8_on($subject); + # strip leading directories + $subject =~ s#^.*[\\/]##; + + my $attachment = MakeMIMEEntity( + Subject => $subject, + Body => "", + AttachmentFieldName => 'Attach' + ); + + $session{'Attachments'} = { %{$session{'Attachments'} || {}}, + $ARGS{'Attach'} => $attachment }; +} +# }}} + +if ( $session{'Attachments'} || + ( $ARGS{'UpdateContent'} ne '' + && $ARGS{'UpdateContent'} ne "-- \n" + . $session{'CurrentUser'}->UserObj->Signature )) { + $ARGS{UpdateAttachments} = $session{'Attachments'}; +} ProcessUpdateMessage( ARGSRef => \%ARGS, Actions => \@results, TicketObj => $Ticket ); +# delete temporary storage entry to make WebUI clean +unless (keys %{$session{'Attachments'}} and $ARGS{'UpdateAttach'}) { + delete $session{'Attachments'}; +} +# }}} + my $Transactions = $Ticket->Transactions; </%INIT> diff --git a/rt/html/SelfService/Elements/MyRequests b/rt/html/SelfService/Elements/MyRequests index 95ede0811..839359aed 100644 --- a/rt/html/SelfService/Elements/MyRequests +++ b/rt/html/SelfService/Elements/MyRequests @@ -49,6 +49,7 @@ $title ||= loc("My [_1] tickets", $friendly_status); my $MyTickets; $MyTickets = new RT::Tickets ($session{'CurrentUser'}); $MyTickets->LimitWatcher(TYPE => 'Requestor', VALUE => $session{'CurrentUser'}->EmailAddress); +$MyTickets->OrderBy(FIELD => 'id', ORDER => 'ASC'); foreach my $status (@status) { diff --git a/rt/html/SelfService/Update.html b/rt/html/SelfService/Update.html index 9ff31775f..9444aa706 100644 --- a/rt/html/SelfService/Update.html +++ b/rt/html/SelfService/Update.html @@ -29,7 +29,24 @@ <&|/l&>Status</&>: <& /Elements/SelectStatus, Name=>"Status", Default => $DefaultStatus &><br> <&|/l&>Subject</&>: <input name="UpdateSubject" size=60 value="Re: <% $Ticket->Subject %>"> <br> -<&|/l&>Attach</&>: <input name="UpdateAttachment" type=file><br> +<table> +<tr> +% if (exists $session{'Attachments'}) { +<TD> +<&|/l&>Attached file</&>: +</TD> +<TD COLSPAN=5> +<&|/l&>Check box to delete</&><BR> +% foreach my $attach_name (keys %{$session{'Attachments'}}) { +<input type="checkbox" name="DeleteAttach-<%$attach_name%>"><%$attach_name%><BR> +% } # end of foreach +</TD> +</TR> +<TR> +% } # end of if +<tr><td align=right><&|/l&>Attach</&>:</td><td><input name="Attach" type="file"><input type="hidden" name="UpdateAttach" value="1"> +</td></tr> +</table> <& /Elements/MessageBox, Name=>"UpdateContent", QuoteTransaction=>$ARGS{QuoteTransaction} &> <INPUT TYPE=HIDDEN NAME=id VALUE="<%$Ticket->Id%>"><br> diff --git a/rt/html/Ticket/Attachment/dhandler b/rt/html/Ticket/Attachment/dhandler index e0f00f57a..ba82b5f2e 100644 --- a/rt/html/Ticket/Attachment/dhandler +++ b/rt/html/Ticket/Attachment/dhandler @@ -65,4 +65,6 @@ $m->out($AttachmentObj->OriginalContent); $m->abort; </%perl> - +<%attr> +AutoFlush => 0 +</%attr> diff --git a/rt/html/Ticket/Create.html b/rt/html/Ticket/Create.html index 5b8c908a1..435447a8f 100644 --- a/rt/html/Ticket/Create.html +++ b/rt/html/Ticket/Create.html @@ -34,55 +34,55 @@ <BR> <& /Elements/TitleBoxStart, contentbg => "#cccccc", title => loc("Create a new ticket") &> <TABLE border=0 cellpadding=0 cellspacing=0> -<TR><TD><&|/l&>Queue</&></TD> -<TD><% $QueueObj->Name %> +<TR><TD class=label><&|/l&>Queue</&>:</TD> +<TD class=value><% $QueueObj->Name %> <INPUT TYPE=HIDDEN NAME=Queue Value="<%$QueueObj->Name%>"> </TD> -<TD><&|/l&>Status</&>: +<TD class=label><&|/l&>Status</&>: </TD> -<TD> +<TD class=value> <& /Elements/SelectStatus, Name => "Status", Default => $ARGS{Status}||'new' &> </TD> -<TD> +<TD class=label> <&|/l&>Owner</&>: </TD> -<TD> +<TD class=value> <& /Elements/SelectOwner, Name => "Owner", QueueObj => $QueueObj, Default => $ARGS{Owner}||undef &> </TD> </TR> <TR> -<TD> +<TD class=label> <&|/l&>Requestors</&>: </TD> -<TD COLSPAN=5> +<TD class=value COLSPAN=5> <INPUT Name="Requestors" Value="<% ($ARGS{Requestors}) || $session{CurrentUser}->EmailAddress %>" SIZE=40> </TD> </TR> <TR> -<TD> +<TD class=labeltop> <&|/l&>Cc</&>: </TD> -<TD COLSPAN=5> -<INPUT NAME="Cc" SIZE=40<% $ARGS{Cc} && " VALUE=\"$ARGS{Cc}\""%>><BR> +<TD class=value COLSPAN=5> +<INPUT NAME="Cc" SIZE=40 VALUE="<% $ARGS{Cc} %>"><BR> <i><font size=-2> <&|/l&>(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)</&></font></i> </TD> </TR> <TR> -<TD> +<TD class=labeltop> <&|/l&>Admin Cc</&>: </TD> -<TD COLSPAN=5> -<INPUT NAME="AdminCc" SIZE=40<% $ARGS{AdminCc} && " VALUE=\"$ARGS{AdminCc}\""%>><BR> +<TD class=value COLSPAN=5> +<INPUT NAME="AdminCc" SIZE=40 VALUE="<% $ARGS{AdminCc} %>"><BR> <i><font size=-2> <&|/l&>(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)</&></font></i> </TD> </TR> <TR> -<TD> +<TD class=label> <&|/l&>Subject</&>: </TD> -<TD COLSPAN=5> +<TD class=value COLSPAN=5> <INPUT Name="Subject" SIZE=60 MAXSIZE=100 value="<%$ARGS{Subject} || ''%>"> </TD> </TR> @@ -93,7 +93,7 @@ </TR> <TR> % if (exists $session{'Attachments'}) { -<TD> +<TD class=label> <&|/l&>Attached file</&>: </TD> <TD COLSPAN=5> @@ -108,7 +108,7 @@ <TD> <&|/l&>Attach file</&>: </TD> -<TD COLSPAN=5> +<TD class=value COLSPAN=5> <INPUT TYPE=FILE NAME="Attach"> <INPUT TYPE=SUBMIT NAME="AddMoreAttach" VALUE="<&|/l&>Add More Files</&>"> </TD> @@ -152,8 +152,8 @@ <TABLE BORDER=0> <TR><TD ALIGN=RIGHT><&|/l&>Priority</&>:</TD><TD><input size=3 name="InitialPriority" value="<% $ARGS{InitialPriority} ? $ARGS{InitialPriority} : $QueueObj->InitialPriority %>"></TD></TR> <TR><TD ALIGN=RIGHT><&|/l&>Final Priority</&>:</TD><TD><input size=3 name="FinalPriority" value="<% $ARGS{FinalPriority} ? $ARGS{FinalPriority} : $QueueObj->FinalPriority %>"></TD></TR> -<TR><TD ALIGN=RIGHT><&|/l&>Time Worked</&>:</TD><TD><input size=3 name="TimeWorked"<% $ARGS{TimeWorked} && " VALUE=\"$ARGS{TimeWorked}\"" %>></TD></TR> -<TR><TD ALIGN=RIGHT><&|/l&>Time Left</&>:</TD><TD><input size=3 name="TimeLeft"<% $ARGS{TimeLeft} && " VALUE=\"$ARGS{TimeLeft}\"" %>></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Time Worked</&>:</TD><TD><input size=3 name="TimeWorked" value="<% $ARGS{TimeWorked} %>"></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Time Left</&>:</TD><TD><input size=3 name="TimeLeft" value="<% $ARGS{TimeLeft} %>"></TD></TR> </TABLE> <& /Elements/TitleBoxEnd &> <br> @@ -162,8 +162,9 @@ color => "#663366" &> <TABLE BORDER=0> -<TR><TD ALIGN=RIGHT><&|/l&>Starts</&>:</TD><TD><input size=10 name="Starts"<% $ARGS{Starts} && " VALUE=\"$ARGS{Starts}\"" %>></TD></TR> -<TR><TD ALIGN=RIGHT><&|/l&>Due</&>:</TD><TD><input size=10 name="Due"<% $ARGS{Due} && " VALUE=\"$ARGS{Due}\"" %>></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Starts</&>:</TD><TD><input size=10 name="Starts" value="<% $ARGS{Starts} %>"></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Due</&>:</TD><TD><input size=10 name="Due" value="<% +$ARGS{Due}%>"></TD></TR> </TABLE> <& /Elements/TitleBoxEnd &> <BR> @@ -176,12 +177,12 @@ <i><&|/l&>(Enter ticket ids or URLs, seperated with spaces)</&></i> <TABLE BORDER=0> -<TR><TD ALIGN=RIGHT><&|/l&>Depends on</&></TD><TD><input size=10 name="new-DependsOn"<% $ARGS{'new-DependsOn'} && " VALUE=\"$ARGS{'new-DependsOn'}\""%>></TD></TR> -<TR><TD ALIGN=RIGHT><&|/l&>Depended on by</&></TD><TD><input size=10 name="DependsOn-new"<% $ARGS{'DependsOn-new'} && " VALUE=\"$ARGS{'DependsOn-new'}\"" %>></TD></TR> -<TR><TD ALIGN=RIGHT><&|/l&>Parents</&></TD><TD><input size=10 name="new-MemberOf"<% $ARGS{'new-MemberOf'} && " VALUE=\"$ARGS{'new-MemberOf'}\"" %>></TD></TR> -<TR><TD ALIGN=RIGHT><&|/l&>Children</&></TD><TD><input size=10 name="MemberOf-new" <% $ARGS{'MemberOf-new'} && " VALUE=\"$ARGS{'MemberOf-new'}\"" %>></TD></TR> -<TR><TD ALIGN=RIGHT><&|/l&>Refers to</&></TD><TD><input size=10 name="new-RefersTo"<% $ARGS{'new-RefersTo'} && " VALUE=\"$ARGS{'new-MemberOf'}\"" %>></TD></TR> -<TR><TD ALIGN=RIGHT><&|/l&>Referred to by</&></TD><TD><input size=10 name="RefersTo-new"<% $ARGS{'RefersTo-new'} && " VALUE=\"$ARGS{'RefersTo-new'}\"" %>></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Depends on</&></TD><TD><input size=10 name="new-DependsOn" value="<% $ARGS{'new-DependsOn'} %>"></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Depended on by</&></TD><TD><input size=10 name="DependsOn-new" value="<% $ARGS{'DependsOn-new'} %>"></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Parents</&></TD><TD><input size=10 name="new-MemberOf" value="<% $ARGS{'new-MemberOf'} %>"></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Children</&></TD><TD><input size=10 name="MemberOf-new" value="<% $ARGS{'MemberOf-new'} %>"></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Refers to</&></TD><TD><input size=10 name="new-RefersTo" value="<% $ARGS{'new-RefersTo'} %>"></TD></TR> +<TR><TD ALIGN=RIGHT><&|/l&>Referred to by</&></TD><TD><input size=10 name="RefersTo-new" value="<% $ARGS{'RefersTo-new'} %>"></TD></TR> </TABLE> @@ -200,10 +201,20 @@ <BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR> <%INIT> + + + my $QueueObj = new RT::Queue($session{'CurrentUser'}); $QueueObj->Load($Queue) || Abort(loc("Queue could not be loaded.")); my $CFs = $QueueObj->CustomFields(); +if ($QueueObj->DefaultDueIn) { + my $default_due = RT::Date->new($session{'CurrentUser'}); + $default_due->SetToNow(); + $default_due->AddDays($QueueObj->DefaultDueIn); + $ARGS{'Due'} = $default_due->ISO(); +} + # {{{ deal with deleting uploaded attachments foreach my $key (keys %ARGS) { if ($key =~ m/^DeleteAttach-(.+)$/) { @@ -218,8 +229,6 @@ if ($ARGS{'Attach'}) { # attachment? my $subject = "$ARGS{'Attach'}"; - # since CGI.pm deutf8izes the magic field, we need to add it back. - Encode::_utf8_on($subject); # strip leading directories $subject =~ s#^.*[\\/]##; diff --git a/rt/html/Ticket/Display.html b/rt/html/Ticket/Display.html index cf32dce9d..276cee62a 100644 --- a/rt/html/Ticket/Display.html +++ b/rt/html/Ticket/Display.html @@ -22,20 +22,20 @@ %# %# END LICENSE BLOCK <& /Elements/Header, - Title => loc("#[_1]: [_2]", $Ticket->Id, $Ticket->Subject) &> + Title => loc("#[_1]: [_2]", $TicketObj->Id, $TicketObj->Subject) &> <& /Ticket/Elements/Tabs, - Ticket => $Ticket, - current_tab => 'Ticket/Display.html?id='.$Ticket->id, - Title => loc("#[_1]: [_2]", $Ticket->Id, $Ticket->Subject) &> + Ticket => $TicketObj, + current_tab => 'Ticket/Display.html?id='.$TicketObj->id, + Title => loc("#[_1]: [_2]", $TicketObj->Id, $TicketObj->Subject) &> <& /Elements/ListActions, actions => \@Actions &> -<& /Ticket/Elements/ShowSummary, Ticket => $Ticket &> +<& /Ticket/Elements/ShowSummary, Ticket => $TicketObj &> <BR> <& /Ticket/Elements/ShowHistory , - Ticket => $Ticket, + Ticket => $TicketObj, Collapsed => $ARGS{'Collapsed'}, ShowHeaders => $ARGS{'ShowHeaders'} &> @@ -45,14 +45,13 @@ $id => undef $Create => undef $ShowHeaders => undef $Collapsed => undef +$TicketObj => undef </%ARGS> <%INIT> - my ($linkid, $message, $tid, $Ticket, @Actions); + my ($linkid, $message, $tid, @Actions); -$Ticket = new RT::Ticket($session{'CurrentUser'}); - -unless ($id) { +unless ($id || $TicketObj) { Abort('No ticket specified'); } @@ -67,47 +66,50 @@ if ($ARGS{'id'} eq 'new') { unless ($Queue->CurrentUserHasRight('CreateTicket')) { Abort('You have no permission to create tickets in that queue.'); } - ($Ticket, @Actions) = - CreateTicket(Attachments => $session{'Attachments'}, %ARGS); + ($TicketObj, @Actions) = + CreateTicket(Attachments => $session{'Attachments'}, %ARGS); delete $session{'Attachments'}; - unless ($Ticket->CurrentUserHasRight('ShowTicket')) { - Abort("No permission to view newly created ticket #".$Ticket->id."."); - } + unless ($TicketObj->CurrentUserHasRight('ShowTicket')) { + Abort("No permission to view newly created ticket #".$TicketObj->id."."); + } # }}} -} +} else { + if (!$TicketObj) { -else { - $Ticket = LoadTicket($ARGS{'id'}); - unless ($Ticket->CurrentUserHasRight('ShowTicket')) { - Abort("No permission to view ticket"); - } + $TicketObj = RT::Ticket->new($session{'CurrentUser'}); + $TicketObj = LoadTicket($ARGS{'id'}); + unless ($TicketObj->CurrentUserHasRight('ShowTicket')) { + Abort("No permission to view ticket"); + } + } -if (defined $ARGS{'Action'}) { - if ($ARGS{'Action'} =~ /^(Steal|Kill|Take|SetTold)$/) { - my $action = $1; - my ($res, $msg)=$Ticket->$action(); - push(@Actions, $msg); - } -} + if (defined $ARGS{'Action'}) { + if ($ARGS{'Action'} =~ /^(Steal|Kill|Take|SetTold)$/) { + my $action = $1; + my ($res, $msg)=$TicketObj->$action(); + push(@Actions, $msg); + } + } - if ( $ARGS{'UpdateContent'} ) { + if ( $ARGS{'UpdateContent'} || $session{'Attachments'}) { $ARGS{'UpdateContent'} =~ s/\r\n/\n/g; - if ( $ARGS{'UpdateContent'} ne '' - && $ARGS{'UpdateContent'} ne "-- \n" - . $session{'CurrentUser'}->UserObj->Signature ) { + if ( $session{'Attachments'} || + ( $ARGS{'UpdateContent'} ne '' + && $ARGS{'UpdateContent'} ne "-- \n" + . $session{'CurrentUser'}->UserObj->Signature )) { $ARGS{UpdateAttachments} = $session{'Attachments'}; ProcessUpdateMessage( ARGSRef => \%ARGS, Actions => \@Actions, - TicketObj => $Ticket ); + TicketObj => $TicketObj ); delete $session{'Attachments'}; } } -#Process status updates -my @BasicActions = ProcessTicketBasics(ARGSRef => \%ARGS, TicketObj=>$Ticket); -my @results = ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => \%ARGS); + #Process status updates + my @BasicActions = ProcessTicketBasics(ARGSRef => \%ARGS, TicketObj=>$TicketObj); + my @results = ProcessTicketLinks( TicketObj => $TicketObj, ARGSRef => \%ARGS); -push (@Actions, @BasicActions, @results); + push (@Actions, @BasicActions, @results); } </%INIT> diff --git a/rt/html/Ticket/Elements/AddWatchers b/rt/html/Ticket/Elements/AddWatchers index e9f651593..96dd38f08 100644 --- a/rt/html/Ticket/Elements/AddWatchers +++ b/rt/html/Ticket/Elements/AddWatchers @@ -77,6 +77,7 @@ my ($msg, $Users, $Groups); if ($UserString) { $Users = RT::Users->new($session{'CurrentUser'}); $Users->Limit(FIELD => $UserField, VALUE => $UserString, OPERATOR => $UserOp); + $Users->LimitToPrivileged if $PrivilegedOnly; } if ($GroupString) { @@ -94,4 +95,5 @@ $UserString => undef $GroupField => 'Name' $GroupOp => '=' $GroupString => undef +$PrivilegedOnly => undef </%ARGS> diff --git a/rt/html/Ticket/Elements/EditCustomField b/rt/html/Ticket/Elements/EditCustomField index 1fc7d4388..16348061e 100644 --- a/rt/html/Ticket/Elements/EditCustomField +++ b/rt/html/Ticket/Elements/EditCustomField @@ -30,6 +30,8 @@ size="<%$Cols%>" % if ($TicketObj) { value="<%$Values->Count ? $Values->First->Content : ''%>" +% } elsif ($Default) { + value="<%$Default ? $Default : ''%>" % } > % } elsif ($CustomField->Type eq 'FreeformMultiple') { @@ -38,6 +40,8 @@ % while (my $value = $Values->Next ) { % $content .= $value->Content; % } +% } elsif ($Default) { + value="<%$Default ? $Default : ''%>" % } <input type="hidden" name="<%$NamePrefix%><%$CustomField->Id%>-Values-Magic" value="1"> <textarea cols=<%$Cols%> rows=<%$Rows%> name="<%$NamePrefix%><%$CustomField->Id%>-Values"><%$content%></textarea> diff --git a/rt/html/Ticket/Elements/EditLinks b/rt/html/Ticket/Elements/EditLinks index 7a522dda6..bdb8a6b7d 100644 --- a/rt/html/Ticket/Elements/EditLinks +++ b/rt/html/Ticket/Elements/EditLinks @@ -35,10 +35,8 @@ <td class="labeltop"><&|/l&>Depends on</&>:</td> <td class="value"> % while (my $link = $Ticket->DependsOn->Next) { -% my $member = $link->TargetObj; <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>"> - <a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> - [<%$member->Status%>]<br> + <& ShowLink, URI => $link->TargetURI &><br> % } </td> </tr> @@ -48,8 +46,7 @@ % while (my $link = $Ticket->DependedOnBy->Next) { % my $member = $link->BaseObj; <INPUT TYPE=CHECKBOX NAME="DeleteLink-<%$link->Base%>-<%$link->Type%>-"> - <a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> - [<%$member->Status%>]<br> + <& ShowLink, URI => $link->BaseURI &><br> % } </td> </tr> @@ -57,10 +54,8 @@ <td class="labeltop"><&|/l&>Parents</&>:</td> <td class="value"> % while (my $link = $Ticket->MemberOf->Next) { -% my $member = $link->TargetObj; <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>"> - <a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> - [<%$member->Status%>]<br> + <& ShowLink, URI => $link->TargetURI &><br> % } </td> </tr> @@ -69,9 +64,7 @@ <td class="value"> % while (my $link = $Ticket->Members->Next) { <INPUT TYPE=CHECKBOX NAME="DeleteLink-<%$link->Base%>-<%$link->Type%>-"> -% my $member = $link->BaseObj; - <a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> - [<%$member->Status%>]<br> + <& ShowLink, URI => $link->BaseURI &><br> % } </td> </tr> @@ -80,12 +73,7 @@ <td class="value"> % while (my $link = $Ticket->RefersTo->Next) { <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>"> -% if ($link->TargetURI->IsLocal) { -% my $member = $link->TargetObj; - <a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> [<%$member->Status%>]<br> -% } else { - <A HREF="<%$link->TargetURI->Resolver->HREF%>"><%$link->TargetURI->Resolver->AsString%></A><br> -% } + <& ShowLink, URI => $link->TargetURI &><br> %} </td> </tr> @@ -94,12 +82,7 @@ <td class="value"> % while (my $link = $Ticket->ReferredToBy->Next) { <INPUT TYPE=CHECKBOX NAME="DeleteLink-<%$link->Base%>-<%$link->Type%>-"> -% if ($link->BaseURI->IsLocal) { -% my $member = $link->BaseObj; - <a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> [<%$member->Status%>]<br> -% } else { - <A HREF="<%$link->BaseURI->Resolver->HREF%>"><%$link->BaseURI->Resolver->AsString%></A><br> -%} + <& ShowLink, URI => $link->BaseURI &><br> % } </td> </tr> diff --git a/rt/html/Ticket/Elements/EditPeople b/rt/html/Ticket/Elements/EditPeople index 1ab8f4ace..a1fc0111a 100644 --- a/rt/html/Ticket/Elements/EditPeople +++ b/rt/html/Ticket/Elements/EditPeople @@ -37,7 +37,7 @@ <& AddWatchers, Ticket => $Ticket, UserString => $UserString, UserOp => $UserOp, UserField => $UserField, GroupString => $GroupString, GroupOp => $GroupOp, - GroupField => $GroupField &> + GroupField => $GroupField, PrivilegedOnly => $PrivilegedOnly &> </TD><TD VALIGN=TOP> <h3><&|/l&>Owner</&></h3> <&|/l&>Owner</&>: <& /Elements/SelectOwner, Name => 'Owner', QueueObj => $Ticket->QueueObj, TicketObj => $Ticket, Default => $Ticket->OwnerObj->Id &> @@ -64,5 +64,6 @@ $UserString => undef $GroupField => undef $GroupOp => undef $GroupString => undef +$PrivilegedOnly => undef $Ticket => undef </%ARGS> diff --git a/rt/html/Ticket/Elements/ShowAttachments b/rt/html/Ticket/Elements/ShowAttachments index 22b60d11b..590a011fb 100644 --- a/rt/html/Ticket/Elements/ShowAttachments +++ b/rt/html/Ticket/Elements/ShowAttachments @@ -47,7 +47,7 @@ if ($size) { </%PERL> <li><font <%$fontsize%>> - <A HREF="<%$RT::WebPath%>/Ticket/Attachment/<%$rev->TransactionObj->Id%>/<%$rev->Id%>/<%$rev->Filename%>"><%$rev->CreatedAsString%> (<% $size %>)</a></font></li> + <A HREF="<%$RT::WebPath%>/Ticket/Attachment/<%$rev->TransactionObj->Id%>/<%$rev->Id%>/<%$rev->Filename | u%>"><%$rev->CreatedAsString%> (<% $size %>)</a></font></li> % } % $fontsize='size="-2"'; % } @@ -63,6 +63,9 @@ my %documents; my $transactions = $Ticket->Transactions(); while (my $trans = $transactions->Next()) { my $attachments = $trans->Attachments(); + $attachments->Columns( qw( Id Filename ContentType Headers Subject Parent ContentEncoding ContentType TransactionId) ); + $attachments->Limit(FIELD => 'Filename', OPERATOR => 'IS NOT', VALUE => 'NULL', QUOTEVALUE => 0, ENTRYAGGREGATOR => 'AND'); + $attachments->Limit(FIELD => 'Filename', OPERATOR => '!=', VALUE => '', ENTRYAGGREGATOR => 'AND'); while (my $attach = $attachments->Next()) { next unless ($attach->Filename()); # most recent at the top diff --git a/rt/html/Ticket/Elements/ShowDates b/rt/html/Ticket/Elements/ShowDates index da7f75bb6..b09b4bf7b 100644 --- a/rt/html/Ticket/Elements/ShowDates +++ b/rt/html/Ticket/Elements/ShowDates @@ -21,6 +21,7 @@ %# %# %# END LICENSE BLOCK + <TABLE> <TR> <TD class="label"><&|/l&>Created</&>:</TD> @@ -35,7 +36,7 @@ <TD class="value"><% $Ticket->StartedObj->AsString %></TD> </TR> <TR> - <TD class="label"><&|/l&>Last Contact</&>:</TD> + <TD class="label"><a href="Display.html?id=<%$Ticket->id%>&Action=SetTold"><&|/l&>Last Contact</&></a>:</TD> <TD class="value"><% $Ticket->ToldObj->AsString %></TD> </TR> <TR> @@ -48,9 +49,15 @@ </TR> <TR> <TD class="label"><&|/l&>Updated</&>:</TD> - <TD class="value"><A HREF="#lasttrans"><% $Ticket->LastUpdated ? (loc("[_1] by [_2]", $Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)) : loc("Never") | h %></a></TD> +% my $UpdatedString = $Ticket->LastUpdated ? (loc("[_1] by [_2]", $Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)) : loc("Never"); +% if ($UpdatedLink) { + <TD class="value"><A HREF="#lasttrans"><% $UpdatedString | h %></a></TD> +% } else { + <TD class="value"><% $UpdatedString | h %></TD> +% } </TR> </TABLE> <%ARGS> $Ticket => undef +$UpdatedLink => 1 </%ARGS> diff --git a/rt/html/Ticket/Elements/ShowHistory b/rt/html/Ticket/Elements/ShowHistory index 2958f8706..194be9b37 100644 --- a/rt/html/Ticket/Elements/ShowHistory +++ b/rt/html/Ticket/Elements/ShowHistory @@ -62,7 +62,7 @@ else { % if ($Transactions->IsLast) { <a name="lasttrans"></a> % } - <& ShowTransaction, Ticket => $Ticket, Transaction => $Transaction, ShowHeaders => $ShowHeaders, Collapsed => $Collapsed, RowNum => $i, ShowTitleBarCommands => $ShowTitleBarCommands &> + <& ShowTransaction, Ticket => $Ticket, Transaction => $Transaction, ShowHeaders => $ShowHeaders, Collapsed => $Collapsed, RowNum => $i, ShowTitleBarCommands => $ShowTitleBarCommands, %ARGS &> % } </TABLE> % if ($ShowDisplayModes or $ShowTitle) { diff --git a/rt/html/Ticket/Elements/ShowMessageStanza b/rt/html/Ticket/Elements/ShowMessageStanza index b0998068f..8e3045a36 100644 --- a/rt/html/Ticket/Elements/ShowMessageStanza +++ b/rt/html/Ticket/Elements/ShowMessageStanza @@ -21,6 +21,8 @@ %# %# %# END LICENSE BLOCK +% if (ref($Message)) { +<font color="<%$colors[$Depth]%>"> <%perl> foreach my $stanza (@$Message) { if ( ref $stanza eq "ARRAY" ) { @@ -36,8 +38,16 @@ foreach my $stanza (@$Message) { $content =~ s/\n/<br>/gi; </%perl> -<font color="<%$colors[$Depth]%>"><%$content |n%><br></font> +<%$content |n%><br> % } +% } # end foreach +</font> +% } else { +% my $content = $Message; +% RT::Interface::Web::EscapeUTF8(\$content); +% $m->comp('/Elements/Callback', content => \$content, %ARGS); +% $content =~ s/\n/<br>/gi; +<%$content |n%><br> % } <%INIT> use URI::URL; diff --git a/rt/html/Ticket/Elements/ShowPeople b/rt/html/Ticket/Elements/ShowPeople index 0b8026949..160da70d9 100644 --- a/rt/html/Ticket/Elements/ShowPeople +++ b/rt/html/Ticket/Elements/ShowPeople @@ -27,15 +27,15 @@ <td class="value"><%$Ticket->OwnerObj->Name%></td> </tr> <tr> - <td class="label"><&|/l&>Requestors</&>:</td> + <td class="labeltop"><&|/l&>Requestors</&>:</td> <td class="value"><%$Ticket->RequestorAddresses%></td> </tr> <tr> - <td class="label"><&|/l&>Cc</&>:</td> + <td class="labeltop"><&|/l&>Cc</&>:</td> <td class="value"><%$Ticket->CcAddresses%></td> </tr> <tr> - <td class="label"><&|/l&>AdminCc</&>:</td> + <td class="labeltop"><&|/l&>AdminCc</&>:</td> <td class="value"><%$Ticket->AdminCcAddresses%></td> </tr> </table> diff --git a/rt/html/Ticket/Elements/ShowTransaction b/rt/html/Ticket/Elements/ShowTransaction index f2f89d35c..2d710fcbc 100644 --- a/rt/html/Ticket/Elements/ShowTransaction +++ b/rt/html/Ticket/Elements/ShowTransaction @@ -38,8 +38,6 @@ unless ($Collapsed) { $attachments->GotoFirstItem; while (my $message=$attachments->Next) { - #we don't want to show any empty transactions, unless they have kids - next unless ($message->ContentLength || $message->Children->Count); my ($headers, $quoted); if ($ShowHeaders && ($ShowHeaders == $Ticket->Id)) { @@ -53,11 +51,18 @@ unless ($Collapsed) { eval {$headers =~ s/^([^:]+)(?=:)/loc($1)/em; } # we eval here to catch errors when 5.6 panics } # 13456 is a random # of about the biggest size we want to see inline text - my $MAX_INLINE_BODY = 13456; + # It's here to catch anyone who hasn't updated RT_Config.pm since this + # constant was moved out there. + my $MAX_INLINE_BODY = $RT::MaxInlineBody || 13456; if ($message->ContentType =~ m{^(text/plain|message|text$)}i && $message->ContentLength < $MAX_INLINE_BODY ) { + eval { require Text::Quoted; - $quoted = Text::Quoted::extract($message->Content); + $quoted = Text::Quoted::extract($message->Content); + }; + if ($@) { + $quoted = $message->Content; + } } </%PERL> @@ -69,7 +74,12 @@ unless ($Collapsed) { <PRE> <& ShowMessageHeaders, Headers => $headers, Transaction => $Transaction &> </PRE> +% if (!length($quoted) && $message->ContentType =~ m#^text/#) { +<blockquote><i><&|/l&>Message body not shown because it is too large or is not plain text.</&><br> +<&|/l&>You can access it with the Download button on the right.</&></i></blockquote> +% } else { <& ShowMessageStanza, Depth => 0, Message => $quoted, Transaction => $Transaction &> +% } </span> </TD> <TD VALIGN=TOP ALIGN=RIGHT> @@ -78,7 +88,7 @@ unless ($Collapsed) { <BR> % } <%PERL> -my $size = $message->ContentLength; +my $size = $message->ContentLength or next; if ($size) { if ($size > 1024) { @@ -88,7 +98,7 @@ if ($size) { $size = loc("[_1]b", $size); } </%PERL> -<font size=-1><A HREF="<%$RT::WebPath%>/Ticket/Attachment/<%$Transaction->Id%>/<%$message->Id%>/<%$message->Filename%>"><&|/l&>Download</&> <% $message->Filename|| loc('(untitled)') %></a> <% $size %></font> +<font size=-1><A HREF="<%$AttachPath%>/<%$Transaction->Id%>/<%$message->Id%>/<%$message->Filename | u%>"><&|/l&>Download</&> <% $message->Filename|| loc('(untitled)') %></a> <% $size %></font> % } </TD> </TR> @@ -104,6 +114,7 @@ $ShowHeaders => 0 $Collapsed => undef $ShowTitleBarCommands => 1 $RowNum => 1 +$AttachPath => $RT::WebPath."/Ticket/Attachment" </%ARGS> <%INIT> @@ -147,6 +158,7 @@ if ($Transaction->TimeTaken > 0) { $TimeTaken = $Transaction->TimeTaken." min" } my $attachments = $Transaction->Attachments; +$attachments->Columns( qw( Id Filename ContentType Headers Subject Parent ContentEncoding ContentType TransactionId) ); my $titlebar_commands=' '; diff --git a/rt/html/Ticket/Elements/Tabs b/rt/html/Ticket/Elements/Tabs index 81c92e8c2..cba45df91 100644 --- a/rt/html/Ticket/Elements/Tabs +++ b/rt/html/Ticket/Elements/Tabs @@ -45,11 +45,17 @@ my $id = $Ticket->id(); if ( defined $session{'tickets'} ) { + # we have to update session data if we get new ItemMap + my $updatesession = 1 unless($session{'tickets'}->{'item_map'}); -my $item_map = $session{'tickets'}->ItemMap; + my $item_map = $session{'tickets'}->ItemMap; - # Don't $current_toptab = display prev links if we're on the first ticket + if ($updatesession) { + $session{'i'}++; + $session{'tickets'}->PrepForSerialization(); + } + # Don't $current_toptab = display prev links if we're on the first ticket if ($item_map->{$Ticket->Id}->{prev}) { $searchtabs->{'_a'} = { class => "nav", diff --git a/rt/html/Ticket/Modify.html b/rt/html/Ticket/Modify.html index c97fd0994..e504a3cba 100644 --- a/rt/html/Ticket/Modify.html +++ b/rt/html/Ticket/Modify.html @@ -46,7 +46,7 @@ my $CustomFields = $TicketObj->QueueObj->CustomFields(); $m->comp('/Elements/Callback', TicketObj => $TicketObj, CustomFields => $CustomFields, %ARGS); my @results = ProcessTicketBasics(TicketObj => $TicketObj, ARGSRef => \%ARGS); -my @cf_results = ProcessTicketCustomFieldUpdates(ARGSRef => \%ARGS); +my @cf_results = ProcessTicketCustomFieldUpdates(TicketObj => $TicketObj, ARGSRef => \%ARGS); push (@results, @cf_results); # TODO: display the results, even if we can't display the ticket diff --git a/rt/html/Ticket/ModifyAll.html b/rt/html/Ticket/ModifyAll.html index a50689398..1163f3fa5 100644 --- a/rt/html/Ticket/ModifyAll.html +++ b/rt/html/Ticket/ModifyAll.html @@ -115,7 +115,7 @@ my (@wresults, @results, @dresults, @lresults, @cf_results); unless ($OnlySearchForPeople) { @wresults = ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS); @results = ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS); - @cf_results = ProcessTicketCustomFieldUpdates(ARGSRef => \%ARGS); + @cf_results = ProcessTicketCustomFieldUpdates( TicketObj => $Ticket, ARGSRef => \%ARGS); @dresults = ProcessTicketDates( TicketObj => $Ticket, ARGSRef => \%ARGS); @lresults = ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => \%ARGS); diff --git a/rt/html/Ticket/ModifyPeople.html b/rt/html/Ticket/ModifyPeople.html index 2e41664d9..debd27a97 100644 --- a/rt/html/Ticket/ModifyPeople.html +++ b/rt/html/Ticket/ModifyPeople.html @@ -44,7 +44,7 @@ my (@results, @wresults); my $Ticket = LoadTicket($id); # if we're trying to search for watchers and nothing else -unless ($OnlySearchForPeople) { +unless ($OnlySearchForPeople or $OnlySearchForGroup) { @results = ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS); @wresults = ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS); } @@ -56,6 +56,7 @@ push @results, @wresults; <%ARGS> $OnlySearchForPeople => undef +$OnlySearchForGroup => undef $UserField => undef $UserOp => undef $UserString => undef diff --git a/rt/html/Ticket/Update.html b/rt/html/Ticket/Update.html index e19aacf6a..ad3b21787 100644 --- a/rt/html/Ticket/Update.html +++ b/rt/html/Ticket/Update.html @@ -23,7 +23,7 @@ %# END LICENSE BLOCK <& /Elements/Header, Title => $title &> <& /Ticket/Elements/Tabs, - Ticket => $Ticket , + Ticket => $TicketObj, Title=> $title &> <FORM ACTION="Update.html" NAME="TicketUpdate" @@ -35,20 +35,20 @@ <TABLE> <TR><TD> -<a href="ModifyPeople.html?id=<%$Ticket->Id%>"><&|/l&>Ticket watchers</&></A></TD><TD align=right> +<a href="ModifyPeople.html?id=<%$TicketObj->Id%>"><&|/l&>Ticket watchers</&></A></TD><TD align=right> <&|/l&>Requestor</&>: </TD><TD> -<b><% $Ticket->RequestorAddresses %></b> +<b><% $TicketObj->RequestorAddresses %></b> </TD></TR> <TR><TD> </TD><TD align=right> <&|/l&>Cc</&>: </TD><TD> -<b><% $Ticket->CcAddresses %></b> +<b><% $TicketObj->CcAddresses %></b> </TD></TR> <TR><TD> </TD><TD align=right> <&|/l&>AdminCc</&>: </TD><TD> -<b><% $Ticket->AdminCcAddresses %></b> +<b><% $TicketObj->AdminCcAddresses %></b> </TD></TR> </TR> </TABLE> @@ -60,7 +60,7 @@ <td> <& /Elements/SelectStatus, Name=>"Status", Default => $DefaultStatus &> <&|/l&>Owner</&>: -<& /Elements/SelectOwner, Name=>"Owner", Default => ($ARGS{'Owner'} || $Ticket->OwnerObj->Id()), QueueObj => $Ticket->QueueObj, TicketObj => $Ticket &> +<& /Elements/SelectOwner, Name=>"Owner", Default => ($ARGS{'Owner'} || $TicketObj->OwnerObj->Id()), QueueObj => $TicketObj->QueueObj, TicketObj => $TicketObj &> <&|/l&>Worked</&>: <input size=4 name="UpdateTimeWorked" value="<% $ARGS{UpdateTimeWorked}%>"> <&|/l&>minutes</&></td></tr> <tr><td align=right><&|/l&>Update Type</&>:</td> <td><select name="UpdateType"> @@ -72,7 +72,7 @@ % } </select> </td></tr> -<tr><td align=right><&|/l&>Subject</&>:</td><td> <input name="UpdateSubject" size=60 value="<% ($ARGS{UpdateSubject}) ? $ARGS{UpdateSubject} : $Ticket->Subject()%>"></td></tr> +<tr><td align=right><&|/l&>Subject</&>:</td><td> <input name="UpdateSubject" size=60 value="<% ($ARGS{UpdateSubject}) ? $ARGS{UpdateSubject} : $TicketObj->Subject()%>"></td></tr> <tr><td align=right><&|/l&>Cc</&>:</td><td> <input name="UpdateCc" size=60 value=<% $ARGS{UpdateCc} %>><BR> <i><font size=-2> @@ -101,12 +101,12 @@ value=<% $ARGS{UpdateCc} %>><BR> <& /Elements/Callback, _CallbackName => 'BeforeMessageBox', %ARGS &> % if (exists $ARGS{UpdateContent}) { % delete $ARGS{'QuoteTransaction'}; -<& /Elements/MessageBox, Name=>"UpdateContent", Default=>$ARGS{UpdateContent}, %ARGS&> +<& /Elements/MessageBox, Name=>"UpdateContent", Default=>$ARGS{UpdateContent}, IncludeSignature => 0, %ARGS&> % } else { <& /Elements/MessageBox, Name=>"UpdateContent", %ARGS &> % } </td></tr> - <INPUT TYPE=HIDDEN NAME=id VALUE="<%$Ticket->Id%>"><br> + <INPUT TYPE=HIDDEN NAME=id VALUE="<%$TicketObj->Id%>"><br> </table> @@ -123,10 +123,10 @@ my $CanRespond = 0; my $CanComment = 0; my $title; -my $Ticket = LoadTicket($id); +my $TicketObj = LoadTicket($id); unless($DefaultStatus){ - $DefaultStatus=($ARGS{'Status'} ||$Ticket->Status()); + $DefaultStatus=($ARGS{'Status'} ||$TicketObj->Status()); } if ($DefaultStatus =~ '^new$'){ @@ -134,9 +134,9 @@ if ($DefaultStatus =~ '^new$'){ } if ($DefaultStatus eq 'resolved') { - $title = loc("Resolve ticket #[_1] ([_2])", $Ticket->id, $Ticket->Subject); + $title = loc("Resolve ticket #[_1] ([_2])", $TicketObj->id, $TicketObj->Subject); } else { - $title = loc("Update ticket #[_1] ([_2])", $Ticket->id, $Ticket->Subject); + $title = loc("Update ticket #[_1] ([_2])", $TicketObj->id, $TicketObj->Subject); } # Things needed in the template - we'll do the processing here, just @@ -150,11 +150,11 @@ if (($Action eq 'Comment') or ($ARGS{'UpdateType'} eq 'private')) { } -$CanRespond = 1 if ( $Ticket->CurrentUserHasRight('ReplyToTicket') or - $Ticket->CurrentUserHasRight('ModifyTicket') ); +$CanRespond = 1 if ( $TicketObj->CurrentUserHasRight('ReplyToTicket') or + $TicketObj->CurrentUserHasRight('ModifyTicket') ); -$CanComment = 1 if ( $Ticket->CurrentUserHasRight('CommentOnTicket') or - $Ticket->CurrentUserHasRight('ModifyTicket') ); +$CanComment = 1 if ( $TicketObj->CurrentUserHasRight('CommentOnTicket') or + $TicketObj->CurrentUserHasRight('ModifyTicket') ); # {{{ deal with deleting uploaded attachments @@ -193,7 +193,7 @@ unless (keys %{$session{'Attachments'}} and $ARGS{'UpdateAttach'}) { # }}} if ( exists $ARGS{SubmitTicket} ) { - $m->comp('Display.html', %ARGS); + $m->comp('Display.html', TicketObj => $TicketObj, %ARGS); return; } </%INIT> diff --git a/rt/html/User/Prefs.html b/rt/html/User/Prefs.html index b89fc40ae..c2746a38c 100644 --- a/rt/html/User/Prefs.html +++ b/rt/html/User/Prefs.html @@ -38,21 +38,44 @@ <& /Elements/TitleBoxStart, title => loc('Identity') &> <input type=hidden name="Name" value="<%$UserObj->Name%>"> -<&|/l&>Email</&>: <input name="EmailAddress" value="<%$UserObj->EmailAddress%>"> -<BR> -<&|/l&>Real Name</&>: <input name="RealName" value="<%$UserObj->RealName%>"> -<BR> -<&|/l&>Nickname</&>: <input name="NickName" value="<%$UserObj->NickName%>"> +<table callspacing=0 cellpadding=0> + <tr> + <td class=label><&|/l&>Email</&>: </td> + <td class=value><input name="EmailAddress" value="<%$UserObj->EmailAddress%>"></td> + </tr> + <tr> + <td class=label><&|/l&>Real Name</&>:</td> + <td class=value><input name="RealName" value="<%$UserObj->RealName%>"></td> </tr> + <tr> + <td class=label><&|/l&>Nickname</&>:</td> + <td class=value><input name="NickName" value="<%$UserObj->NickName%>"></td> + </tr> + <tr> + <td class=label><&|/l&>Language</&>:</td> + <td class=value><& /Elements/SelectLang, Name => 'Lang', Default => $UserObj->Lang &></td> + </tr> +</table> <& /Elements/TitleBoxEnd &> <br> <& /Elements/TitleBoxStart, title => loc('Phone numbers') &> -<&|/l&>Residence</&>: <input name="HomePhone" value="<%$UserObj->HomePhone%>" size=13> -<BR> -<&|/l&>Work</&>: <input name="WorkPhone" value="<%$UserObj->WorkPhone%>" size=13> -<BR> -<&|/l&>Mobile</&>: <input name="MobilePhone" value="<%$UserObj->MobilePhone%>" size=13> -<BR> -<&|/l&>Pager</&>: <input name="PagerPhone" value="<%$UserObj->PagerPhone%>" size=13> +<table callspacing=0 cellpadding=0> + <tr> + <td class=label><&|/l&>Residence</&>:</td> + <td class=value><input name="HomePhone" value="<%$UserObj->HomePhone%>" size=13></td> + </tr> + <tr> + <td class=label><&|/l&>Work</&>:</td> + <td class=value><input name="WorkPhone" value="<%$UserObj->WorkPhone%>" size=13></td> + </tr> + <tr> + <td class=label><&|/l&>Mobile</&>:</td> + <td class=value><input name="MobilePhone" value="<%$UserObj->MobilePhone%>" size=13></td> + </tr> + <tr> + <td class=label><&|/l&>Pager</&>:</td> + <td class=value><input name="PagerPhone" value="<%$UserObj->PagerPhone%>" size=13></td> + </tr> +</table> <& /Elements/TitleBoxEnd &> </TD> <TD VALIGN=TOP> @@ -60,44 +83,58 @@ <& /Elements/TitleBoxStart, title => loc('Password') &> <TABLE> <TR> -<TD ALIGN=RIGHT> +<TD class=label> <&|/l&>New Password</&>: </TD> -<TD ALIGN=LEFT> +<TD class=value> <input type=password name="Pass1"> </TD> </TR> -<TR><TD ALIGN=RIGHT> +<TR><TD class=label> <&|/l&>Retype Password</&>: </TD> -<TD> +<TD class=value> <input type=password name="Pass2"> </TD> </TR> </TABLE> -% } <& /Elements/TitleBoxEnd &> +% } </TD> <TR> <TD VALIGN=TOP> <& /Elements/TitleBoxStart, title => loc('Location') &> -<&|/l&>Organization</&>: <input name="Organization" value="<%$UserObj->Organization%>"> -<BR> -<&|/l&>Address1</&>: <input name="Address1" value="<%$UserObj->Address1%>"> -<BR> -<&|/l&>Address2</&>: <input name="Address2" value="<%$UserObj->Address2%>"> -<BR> -<&|/l&>City</&>: <input name="City" value="<%$UserObj->City%>" size=14> - -<&|/l&>State</&>: <input name="State" value="<%$UserObj->State%>" size=3> - -<&|/l&>Zip</&>: <input name="Zip" value="<%$UserObj->Zip%>" size=9> -<BR> -<&|/l&>Country</&>: <input name="Country" value="<%$UserObj->Country%>"> -<BR> - - +<table callspacing=0 cellpadding=0> + <tr> + <td class=label><&|/l&>Organization</&>:</td> + <td class=value><input name="Organization" value="<%$UserObj->Organization%>"></td> + </tr> + <tr> + <td class=label><&|/l&>Address1</&>:</td> + <td class=value><input name="Address1" value="<%$UserObj->Address1%>"></td> + </tr> + <tr> + <td class=label><&|/l&>Address2</&>:</td> + <td class=value><input name="Address2" value="<%$UserObj->Address2%>"></td> + </tr> + <tr> + <td class=label><&|/l&>City</&>:</td> + <td><input name="City" value="<%$UserObj->City%>" size=14></td> + </tr> + <tr> + <td class=label><&|/l&>State</&>:</td> + <td class=value><input name="State" value="<%$UserObj->State%>" size=3></td> + </tr> + <tr> + <td class=label><&|/l&>Zip</&>:</td> + <td class=value><input name="Zip" value="<%$UserObj->Zip%>" size=9></td> + </tr> + <tr> + <td class=label><&|/l&>Country</&>:</td> + <td class=value><input name="Country" value="<%$UserObj->Country%>"></td> + </tr> +</table> <& /Elements/TitleBoxEnd &> </TD> </TR> @@ -147,12 +184,13 @@ if ($UserObj->Id) { Organization RealName NickName Lang EmailEncoding WebEncoding ExternalContactInfoId ContactInfoSystem Gecos ExternalAuthId AuthSystem HomePhone WorkPhone MobilePhone PagerPhone Address1 - Address2 City State Zip Country + Address2 City State Zip Country Lang ); my @fieldresults = UpdateRecordObject ( AttributesRef => \@fields, Object => $UserObj, ARGSRef => \%ARGS ); + $session{'CurrentUser'}->LanguageHandle($Lang) if $Lang; push (@results,@fieldresults); diff --git a/rt/html/autohandler b/rt/html/autohandler index ce8b7569e..b2a407add 100644 --- a/rt/html/autohandler +++ b/rt/html/autohandler @@ -27,18 +27,24 @@ $RT::Handle->ForceRollback() if $RT::Handle->TransactionDepth; -local *session; +local *session unless $m->is_subrequest; # avoid reentrancy, as suggested by masonbook + +# Disable AutoFlush using an attribute +if ($m->request_comp->attr_exists('AutoFlush')) { + $m->autoflush($m->request_comp->attr('AutoFlush')); +} + %ARGS = map { - # if they've passed multiple values, they'll be an array. if they've passed just one, a scalar - # whatever they are, mark them as utf8 + # if they've passed multiple values, they'll be an array. if they've + # passed just one, a scalar whatever they are, mark them as utf8 my $type = ref($_); (!$type) ? Encode::decode(utf8 => $_, Encode::FB_PERLQQ) : - ($type eq 'ARRAY') + ($type eq 'ARRAY') ? [ map { ref($_) ? $_ : Encode::decode(utf8 => $_, Encode::FB_PERLQQ) } @$_ ] : - ($type eq 'HASH') + ($type eq 'HASH') ? { map { ref($_) ? $_ : Encode::decode(utf8 => $_, Encode::FB_PERLQQ) } %$_ } : $_ -} %ARGS; + } %ARGS; if ($ARGS{'Debug'}) { require Time::HiRes; @@ -65,69 +71,95 @@ if ($m->base_comp->path =~ '^/+NoAuth/' || $m->abort(); } -# If RT is configured for external auth, let's get REMOTE_USER -elsif ($RT::WebExternalAuth and length($ENV{'REMOTE_USER'})) { - my $orig_user = $user; - - $user = $ENV{'REMOTE_USER'}; - $session{'CurrentUser'} = RT::CurrentUser->new(); - my $load_method = $RT::WebExternalGecos ? 'LoadByGecos' : 'Load'; - - if ($^O eq 'MSWin32' and $RT::WebExternalGecos) { - my $NodeName = Win32::NodeName(); - $user =~ s/^\Q$NodeName\E\\//i; - } - - $session{'CurrentUser'}->$load_method($user); - - if ($RT::WebExternalAuto and !$session{'CurrentUser'}->Id() ) { - # Create users on-the-fly with default attributes - - my $UserObj = RT::User->new(RT::CurrentUser->new('root')); - - my ($val, $msg) = $UserObj->Create( - %{ref($RT::AutoCreate) ? $RT::AutoCreate : {}}, - Name => $user, - Gecos => $user, - ); - - if ($val) { - $UserObj->SetPrivileged(1); - - if ($^O !~ /^(?:riscos|MacOS|MSWin32|dos|os2)$/) { - # Populate fields with information from Unix /etc/passwd - - my ($comments, $realname) = (getpwnam($user))[5, 6]; - $UserObj->SetComments($comments) if defined $comments; - $UserObj->SetRealName($realname) if defined $realname; +# If RT is configured for external auth, let's go through and get REMOTE_USER +elsif ( $RT::WebExternalAuth ) { + + # do we actually have a REMOTE_USER equivlent? + if ( RT::Interface::Web::WebCanonicalizeInfo() ) { + + my $orig_user = $user; + + $user = RT::Interface::Web::WebCanonicalizeInfo(); + $session{'CurrentUser'} = RT::CurrentUser->new(); + my $load_method = $RT::WebExternalGecos ? 'LoadByGecos' : 'Load'; + + if ($^O eq 'MSWin32' and $RT::WebExternalGecos) { + my $NodeName = Win32::NodeName(); + $user =~ s/^\Q$NodeName\E\\//i; + } + + $session{'CurrentUser'}->$load_method($user); + + if ($RT::WebExternalAuto and !$session{'CurrentUser'}->Id() ) { + # Create users on-the-fly + + my $UserObj = RT::User->new(RT::CurrentUser->new('root')); + + my ($val, $msg) = $UserObj->Create( + %{ref($RT::AutoCreate) ? $RT::AutoCreate : {}}, + Name => $user, + Gecos => $user, + ); + + if ($val) { + + # now get user specific information, to better create our user. + my $new_user_info = RT::Interface::Web::WebExternalAutoInfo($user); + + # set the attributes that have been defined. + # FIXME: this is a horrible kludge. I'm sure there's something cleaner + foreach my $attribute ('Name', 'Comments', 'Signature', 'EmailAddress', + 'PagerEmailAddress', 'FreeformContactInfo', + 'Organization', 'Disabled', 'Privileged', + 'RealName', 'NickName', 'Lang', 'EmailEncoding', + 'WebEncoding', 'ExternalContactInfoId', + 'ContactInfoSystem', 'ExternalAuthId', 'Gecos', + 'HomePhone', 'WorkPhone', 'MobilePhone', + 'PagerPhone', 'Address1', 'Address2', 'City', + 'State', 'Zip', 'Country') { + + my $method = "Set$attribute"; + $UserObj->$method($new_user_info->{$attribute}) + if( defined $new_user_info->{$attribute} ); + } + $session{'CurrentUser'}->Load($user); } - elsif ($^O eq 'MSWin32' and eval 'use Net::AdminMisc; 1') { - # Populate fields with information from NT domain controller + else { + # we failed to successfully create the user. abort abort abort. + delete $session{'CurrentUser'}; + $m->abort() unless $RT::WebFallbackToInternalAuth; + $m->comp('/Elements/Login', %ARGS, + Error=> loc('Cannot create user: [_1]', $msg)); } - - $session{'CurrentUser'}->Load($user); } - else { + + unless ( $session{'CurrentUser'}->Id() ) { delete $session{'CurrentUser'}; - $m->abort() unless $RT::WebFallbackToInternalAuth; - $m->comp('/Elements/Login', %ARGS, Error=> loc('Cannot create user: [_1]', $msg)); + $user = $orig_user; + + if ( $RT::WebExternalOnly ) { + $m->comp('/Elements/Login', %ARGS, + Error=> loc('You are not an authorized user')); + $m->abort(); + } } } - - unless ( $session{'CurrentUser'}->Id() ) { - delete $session{'CurrentUser'}; - $user = $orig_user; - - if ( $RT::WebExternalOnly ) { - $m->comp('/Elements/Login', %ARGS, Error=> loc('You are not an authorized user')); - $m->abort(); + elsif ($RT::WebFallbackToInternalAuth) { + unless (defined($session{'CurrentUser'})) { + $m->comp('/Elements/Login', %ARGS, + Error=> loc('XXX CHANGEME You are not an authorized user')); + $m->abort(); } + } else { + # WebExternalAuth is set, but we don't have a REMOTE_USER. abort + delete $session{'CurrentUser'} if defined $session{'CurrentUser'}; } } delete $session{'CurrentUser'} unless $session{'CurrentUser'} and defined $session{'CurrentUser'}->Id; + # Process per-page authentication callbacks $m->comp('/Elements/Callback', %ARGS, _CallbackName => 'Auth'); diff --git a/rt/html/index.html b/rt/html/index.html index 39eac8d61..798972d94 100644 --- a/rt/html/index.html +++ b/rt/html/index.html @@ -53,7 +53,8 @@ if ( $ARGS{'q'} ) { $session{'tickets'} = RT::Tickets->new( $session{'CurrentUser'} ); if ( $query =~ m/\@/ ) { - $session{'tickets'}->LimitRequestor( VALUE => $query, + $session{'tickets'}->LimitWatcher( VALUE => $query, + TYPE => 'Requestor', OPERATOR => '=', ); $m->redirect("$RT::WebPath/Search/Listing.html"); } |