[Rt-commit] r7484 - in rt/branches/3.7-EXPERIMENTAL-TUNIS: . html/Admin/Elements html/Admin/Queues html/Admin/Users html/Prefs html/Ticket html/Ticket/Elements html/Widgets/Form lib/RT lib/RT/Action lib/RT/Condition lib/RT/Crypt lib/RT/Interface

jesse at bestpractical.com jesse at bestpractical.com
Tue Apr 10 16:46:25 EDT 2007


Author: jesse
Date: Tue Apr 10 16:46:22 2007
New Revision: 7484

Added:
   rt/branches/3.7-EXPERIMENTAL-TUNIS/docs/using_forms_widgets.pod
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Elements/ShowKeyInfo
Modified:
   rt/branches/3.7-EXPERIMENTAL-TUNIS/   (props changed)
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Queues/Modify.html
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Users/Modify.html
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Prefs/Other.html
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Create.html
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Elements/ShowTransaction
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Forward.html
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Boolean
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Integer
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Select
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/SendEmail.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Condition/OwnerChange.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Crypt/GnuPG.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Email.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Ticket_Overlay.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Tickets_Overlay.pm

Log:
 r54305 at pinglin (orig r7378):  ruz | 2007-03-27 20:01:17 -0400
  r4823 at cubic-pc:  cubic | 2007-03-28 04:01:01 +0400
  * if we cache aliases then we should not apply limits multiple times
 
 r54307 at pinglin (orig r7380):  ruz | 2007-03-28 15:47:52 -0400
  r4825 at cubic-pc:  cubic | 2007-03-28 23:45:49 +0400
  * add results to the forward page
 
 r54321 at pinglin (orig r7381):  ruz | 2007-03-29 11:03:47 -0400
  r4827 at cubic-pc:  cubic | 2007-03-29 02:18:46 +0400
  * add GetPassphrase function
 
 r54322 at pinglin (orig r7382):  ruz | 2007-03-29 11:03:56 -0400
  r4828 at cubic-pc:  cubic | 2007-03-29 02:19:08 +0400
  * update docs
 
 r54323 at pinglin (orig r7383):  ruz | 2007-03-29 11:04:05 -0400
  r4829 at cubic-pc:  cubic | 2007-03-29 10:40:47 +0400
  * docs update
 
 r54324 at pinglin (orig r7384):  ruz | 2007-03-29 11:04:21 -0400
  r4830 at cubic-pc:  cubic | 2007-03-29 10:42:38 +0400
  * use CamelCase args in SendEmail with backward compatibility
 
 r54325 at pinglin (orig r7385):  ruz | 2007-03-29 11:04:29 -0400
  r4831 at cubic-pc:  cubic | 2007-03-29 10:48:51 +0400
  * pass the current ticket to mailer
 
 r54326 at pinglin (orig r7386):  ruz | 2007-03-29 11:04:40 -0400
  r4832 at cubic-pc:  cubic | 2007-03-29 10:49:26 +0400
  * use camel case
 
 r54327 at pinglin (orig r7387):  ruz | 2007-03-29 11:04:55 -0400
  r4833 at cubic-pc:  cubic | 2007-03-29 10:50:24 +0400
  * fetch ticket from transaction if the former is not provided
 
 r54328 at pinglin (orig r7388):  ruz | 2007-03-29 11:05:04 -0400
  r4834 at cubic-pc:  cubic | 2007-03-29 10:58:07 +0400
  * if ticket is there and Queue dictates to sign or encrypt then
    do it
 
 r54329 at pinglin (orig r7389):  ruz | 2007-03-29 11:05:21 -0400
  r4835 at cubic-pc:  cubic | 2007-03-29 17:43:28 +0400
  * add GetPublicKeyInfo and helpers
 
 r54330 at pinglin (orig r7390):  ruz | 2007-03-29 11:05:31 -0400
  r4836 at cubic-pc:  cubic | 2007-03-29 17:43:54 +0400
  * show user's public key if it's exists
 
 r54331 at pinglin (orig r7391):  ruz | 2007-03-29 11:52:48 -0400
  r4847 at cubic-pc:  cubic | 2007-03-29 19:10:50 +0400
  * use an address as argument instead of an user's object
 
 r54332 at pinglin (orig r7392):  ruz | 2007-03-29 11:53:02 -0400
  r4848 at cubic-pc:  cubic | 2007-03-29 19:45:47 +0400
  * parse info about private keys
 
 r54333 at pinglin (orig r7393):  ruz | 2007-03-29 11:53:12 -0400
  r4849 at cubic-pc:  cubic | 2007-03-29 19:47:03 +0400
  * if trust char is empty then use '-' as substitution as
    this value is closest one.
 
 r54334 at pinglin (orig r7394):  ruz | 2007-03-29 11:53:36 -0400
  r4850 at cubic-pc:  cubic | 2007-03-29 19:47:34 +0400
  * add GetPrivateKeyInfo
 
 r54335 at pinglin (orig r7395):  ruz | 2007-03-29 11:54:11 -0400
  r4851 at cubic-pc:  cubic | 2007-03-29 19:47:58 +0400
  * Elements/ShowPrivateKeyInfo
 
 r54336 at pinglin (orig r7396):  ruz | 2007-03-29 11:54:21 -0400
  r4852 at cubic-pc:  cubic | 2007-03-29 19:48:55 +0400
  * show information about queue's private keys
 
 r54337 at pinglin (orig r7397):  ruz | 2007-03-29 11:54:28 -0400
  r4853 at cubic-pc:  cubic | 2007-03-29 19:52:25 +0400
  * move ShowPublicKeyInfo
  ** sync it with ShowPrivateKeyInfo
 
 r54338 at pinglin (orig r7398):  ruz | 2007-03-29 21:27:05 -0400
  r4865 at cubic-pc:  cubic | 2007-03-30 05:12:16 +0400
  * add _ParseDate
 
 r54339 at pinglin (orig r7399):  ruz | 2007-03-29 21:27:14 -0400
  r4866 at cubic-pc:  cubic | 2007-03-30 05:13:09 +0400
  * add support for uid records in key lists
 
 r54340 at pinglin (orig r7400):  ruz | 2007-03-29 21:27:21 -0400
  r4867 at cubic-pc:  cubic | 2007-03-30 05:13:40 +0400
  * parse dates
 
 r54341 at pinglin (orig r7401):  ruz | 2007-03-29 21:27:28 -0400
  r4868 at cubic-pc:  cubic | 2007-03-30 05:14:45 +0400
  * use fixed-list-mode, so each user has own uid record
 
 r54342 at pinglin (orig r7402):  ruz | 2007-03-29 21:27:45 -0400
  r4869 at cubic-pc:  cubic | 2007-03-30 05:15:37 +0400
  * update according to new API, now we support keys with multiple uids
 
 r54343 at pinglin (orig r7403):  ruz | 2007-03-29 21:28:03 -0400
  r4870 at cubic-pc:  cubic | 2007-03-30 05:23:11 +0400
  * generalize comp for any type of keys
 
 r54344 at pinglin (orig r7404):  ruz | 2007-03-29 21:28:10 -0400
  r4871 at cubic-pc:  cubic | 2007-03-30 05:26:26 +0400
  * use ShowKeyInfo for secret and public keys
 
 r54486 at pinglin (orig r7446):  ruz | 2007-04-04 17:50:24 -0400
  r4879 at cubic-pc:  cubic | 2007-04-03 18:29:01 +0400
  * delete empty row in a table
 
 r54487 at pinglin (orig r7447):  ruz | 2007-04-04 17:50:33 -0400
  r4880 at cubic-pc:  cubic | 2007-04-03 18:35:59 +0400
  * add Sign/Encrypt boxes to Create page
 
 r54488 at pinglin (orig r7448):  ruz | 2007-04-04 17:50:48 -0400
  r4881 at cubic-pc:  cubic | 2007-04-03 19:09:32 +0400
  * call CanonicalizeEmailAddress as class method as we do in other places
 
 r54489 at pinglin (orig r7449):  ruz | 2007-04-04 17:51:04 -0400
  r4882 at cubic-pc:  cubic | 2007-04-04 02:26:41 +0400
  * get rid of uninit warning
 
 r54490 at pinglin (orig r7450):  ruz | 2007-04-04 17:51:12 -0400
  r4883 at cubic-pc:  cubic | 2007-04-04 02:29:36 +0400
  Boolean widget
  * get rid of Boolean- prefix in name of field
  * add InputOnly method
  * use magic field trick to save a state of the checkbox
 
 r54492 at pinglin (orig r7451):  ruz | 2007-04-04 17:51:20 -0400
  r4884 at cubic-pc:  cubic | 2007-04-04 02:32:15 +0400
  * use widgets for Sign and Encrypt args
 
 r54493 at pinglin (orig r7452):  ruz | 2007-04-04 17:51:38 -0400
  r4885 at cubic-pc:  cubic | 2007-04-04 02:33:20 +0400
  * move field's comment into own cell
 
 r54494 at pinglin (orig r7453):  ruz | 2007-04-04 17:51:47 -0400
  r4886 at cubic-pc:  cubic | 2007-04-04 02:33:58 +0400
  * tidy, before change
 
 r54495 at pinglin (orig r7454):  ruz | 2007-04-04 17:52:06 -0400
  r4887 at cubic-pc:  cubic | 2007-04-04 14:48:11 +0400
  * add support for Empty option in Boolean widget.
    display such widget with three radio buttons and Process method returns
    0, 1 or undef if value is empty
    
 
 r54496 at pinglin (orig r7455):  ruz | 2007-04-04 17:52:14 -0400
  r4888 at cubic-pc:  cubic | 2007-04-04 14:53:45 +0400
  * remove NamePrefix and make Name mandatory argument
 
 r54497 at pinglin (orig r7456):  ruz | 2007-04-04 17:52:32 -0400
  r4889 at cubic-pc:  cubic | 2007-04-04 14:58:36 +0400
  * get rid of Integer- prefix in name
 
 r54498 at pinglin (orig r7457):  ruz | 2007-04-04 17:52:41 -0400
  r4890 at cubic-pc:  cubic | 2007-04-04 17:04:04 +0400
  * make Name mandatory arg
  * add InputOnly method
 
 r54500 at pinglin (orig r7458):  ruz | 2007-04-04 17:52:58 -0400
  r4891 at cubic-pc:  cubic | 2007-04-04 17:04:46 +0400
  * fix empty value handling
 
 r54501 at pinglin (orig r7459):  ruz | 2007-04-04 17:53:07 -0400
  r4892 at cubic-pc:  cubic | 2007-04-04 17:33:13 +0400
  * add InputOnly method
 
 r54502 at pinglin (orig r7460):  ruz | 2007-04-04 17:53:24 -0400
  r4893 at cubic-pc:  cubic | 2007-04-04 17:39:16 +0400
  * in Widget/Form/*:Process rename CurrenValue argument to DefaultValue,
    as old one is really confusing
 
 r54503 at pinglin (orig r7461):  ruz | 2007-04-04 17:53:35 -0400
  r4894 at cubic-pc:  cubic | 2007-04-05 01:38:20 +0400
  * add docs for html/Widgets/Form/*
  * use Default instead of Empty
 
 r54504 at pinglin (orig r7462):  ruz | 2007-04-04 17:53:57 -0400
  r4895 at cubic-pc:  cubic | 2007-04-05 01:38:42 +0400
  * drop uninit warning
 
 r54742 at pinglin (orig r7482):  ruz | 2007-04-09 17:48:37 -0400
  r4913 at cubic-pc:  cubic | 2007-04-06 16:37:26 +0400
  * force scalar context
 


Added: rt/branches/3.7-EXPERIMENTAL-TUNIS/docs/using_forms_widgets.pod
==============================================================================
--- (empty file)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/docs/using_forms_widgets.pod	Tue Apr 10 16:46:22 2007
@@ -0,0 +1,113 @@
+=head1 Using widgets F<html/Widgets/Form*>
+
+This widgets was implemented to address several common issues in handling
+request arguments and allow developers to avoid reinventing the wheel.
+
+=head2 General info
+
+Each component shows widget by default and has two methods: Process and 
+InputOnly. The first one method process arguments and return new value
+of a parametr. The second one is helper that shows only form elements
+with minimum of required text labels.
+
+So you show a widget with:
+    <& /Widgets/Form/Integer,
+        Name => 'NameOfInputElement',
+        Description => 'Input integer',
+    &>
+
+You can show only C<input> box using:
+    <& /Widgets/Form/Integer:InputOnly,
+        Name => 'NameOfInputElement',
+    &>
+
+In such a simple case you even can avoid processing. Yeah, most probably
+you want to check if value is really integer, but these widgets don't
+do validation for you, but they are more about fetching values from
+hash of arguments, showing these values to user and preserving state
+of value between form reloads (see below).
+
+=head2 Processing
+
+Processing is required when you use L<extended features|/Extendent features>,
+such as Default, Multiple or Alternative.
+
+To process arguments of a request you have to do the following:
+    $ARGS{'NameOfInputElement'} = $m->comp(
+        '/Widgets/Form/Integer:Process',
+        Arguments => \%ARGS,
+        Name      => 'NameOfInputElement',
+    );
+
+The method returns processed value in canonical form. For different widgets
+a canonical form is different and depends on activated features, so you must
+always activate the same features during showing a widget and processing
+results.
+
+=head2 Extendent features
+
+=head3 Default value
+
+If C<Default> argument is true then widgets expect that there is some
+default value for argument if user fills nothing. 'Nothing' in each
+widget is different, for example in select box it's special option
+which is always the first one, in integer box string '' means empty
+value, but boolean box uses radio buttons in this case with three
+options: Yes, No and Default.
+
+Each widget that supports C<Default> feature as well has C<DefaultLabel> and
+C<DefaultValue> arguments.
+
+=head4 Processing and showing with activated Default feature
+
+When this option is activated then C<Process> method returns undef
+value if user selected default value. So for integer box it's empty
+string and so on.
+
+As well when you show a widget you should pass undef as C<CurrentValue>
+to inform widget that the current value is default one.
+
+As all methods of a widget are consistent in this behaviour so you
+shouldn't care much about that, but this allows you to implement
+custom actions if processing returned undef, for example delete user's
+preference record instead of updating it (default value may change later to).
+
+=head4 C<DefaultValue> when C<Default> is not active
+
+DefaultValue argument is still actual in the Process method even if
+C<Default> is not true. This argument defines intial value. If value
+of a key in Arguments is not defined then it's treated as intial state
+and the method returns default value.
+
+=head3 Multiple and Alternative
+
+These options are only supported by the select widget.
+
+TODO: Add more info
+
+=head2 Implementation details
+
+=head3 Boolean widget
+
+This widget a little bit tricky. When you use Default option then
+things are simple and you see three radio buttons, but in other
+case we use a checkbox. But as you know browsers don't pass unchecked
+boxes to server, so arguments of a request has no entry for them.
+
+In the latter case it's hard to figure out case when user unselected
+value. Imagine form with a checkbox, you want show it checked by
+default and as well form is reloadable (like Reply forms that have
+"Add Another File" buttons). User uncheck the box and then upload
+file, in this case you want to show user's choice instead of default,
+but browser doesn't send any value and you can not figure out if
+it's initial state or page reload. To solve this problem we use magic
+hidden input field with the same name as the box and value equal to
+zero (0). Mason folds arguments with the same name into array refs, so
+we get 0 if box is unchecked and [0, 1] if box is checked. An array
+reference is true value and 0 is defined value so we know that it's
+not initial state and avoid switching back to default. As well this
+trick works good in a case when you want show a link to a page and
+define default choice for some boolean argument, you don't need
+to set argument twice, you just set it to true value (for ex. 1) and
+things just work.
+

Added: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Elements/ShowKeyInfo
==============================================================================
--- (empty file)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Elements/ShowKeyInfo	Tue Apr 10 16:46:22 2007
@@ -0,0 +1,44 @@
+<&| /Widgets/TitleBox, title => $title &>
+% if ( $res{'exit_code'} || !keys %{ $res{'info'} } ) {
+<% loc('No keys for this address') %>
+% } else {
+<table>
+
+% unless ( $Type eq 'private' ) {
+<tr><th><% loc('Trust') %>:</th>  <td><% loc( $res{'info'}{'Trust'} ) %></td></tr>
+% }
+
+<tr><th><% loc('Created') %>:</th>
+<td><% $res{'info'}{'Created'}? $res{'info'}{'Created'}->AsString( Time => 0 ): loc('never') %></td></tr>
+
+<tr><th><% loc('Expire') %>:</th>
+<td><% $res{'info'}{'Expire'}? $res{'info'}{'Expire'}->AsString( Time => 0 ): loc('never') %></td></tr>
+
+% foreach my $uinfo( @{ $res{'info'}{'User'} } ) {
+<tr><th><% loc('User (created - expire)') %>:</th>
+<td><% $uinfo->{'String'} %>\
+(<% $uinfo->{'Created'}->AsString( Time => 0 ) %> - \
+<% $uinfo->{'Expire'}? $uinfo->{'Expire'}->AsString( Time => 0 ): loc('never') %>)
+</td></tr>
+% }
+
+</table>
+% }
+</&>
+
+<%ARGS>
+$EmailAddress
+$Type => 'public'
+</%ARGS>
+<%INIT>
+require RT::Crypt::GnuPG;
+my %res = RT::Crypt::GnuPG::GetKeyInfo( $EmailAddress, $Type );
+
+my $title;
+unless ( $Type eq 'private' ) {
+    $title = loc('GnuPG public key(s) for [_1]', $EmailAddress);
+} else {
+    $title = loc('GnuPG private key(s) for [_1]', $EmailAddress);
+}
+
+</%INIT>

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Queues/Modify.html
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Queues/Modify.html	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Queues/Modify.html	Tue Apr 10 16:46:22 2007
@@ -94,6 +94,13 @@
 % $m->callback( %ARGS, QueueObj => $QueueObj, results => \@results );
 </td></tr>
 
+% if ( my $email = $QueueObj->CorrespondAddress || RT->Config->Get('CorrespondAddress') ) {
+<tr><td colspan="4"><& /Admin/Elements/ShowKeyInfo, Type => 'private', EmailAddress => $email &></td></tr>
+% }
+% if ( my $email = $QueueObj->CommentAddress || RT->Config->Get('CommentAddress') ) {
+<tr><td colspan="4"><& /Admin/Elements/ShowKeyInfo, Type => 'private', EmailAddress => $email &></td></tr>
+% }
+
 </table>
 <& /Elements/Submit, Label => loc('Save Changes') &>
 </form>

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Users/Modify.html
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Users/Modify.html	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Admin/Users/Modify.html	Tue Apr 10 16:46:22 2007
@@ -232,6 +232,10 @@
 </&>
 % }
 
+% if ( my $email = $UserObj->EmailAddress ) {
+<& /Admin/Elements/ShowKeyInfo, EmailAddress => $email &>
+% }
+
 </td>
 </tr>
 </table>
@@ -373,7 +377,7 @@
 }
 
 
-# {{{ Do some setup for the ui
+# Do some setup for the ui
 unless ( $UserObj->id && $UserObj->Disabled ) {
     $EnabledChecked ="CHECKED";
 }
@@ -382,8 +386,6 @@
     $PrivilegedChecked = "CHECKED";
 }
 
-# }}}
-
 # set the id, so the the menu will have the right info, this needs to
 # be done here to avoid creating and then modifying a user
 $id = $UserObj->Id;

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Prefs/Other.html
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Prefs/Other.html	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Prefs/Other.html	Tue Apr 10 16:46:22 2007
@@ -11,10 +11,10 @@
 % foreach my $option( RT->Config->Options( Section => $section ) ) {
 % my $meta = RT->Config->Meta( $option );
 <& $meta->{'Widget'},
-    Empty        => 1,
+    Default      => 1,
     %{ $localize_meta->( $meta->{'WidgetArguments'} ) },
     Name         => $option,
-    EmptyValue   => scalar RT->Config->Get( $option ),
+    DefaultValue => scalar RT->Config->Get( $option ),
     CurrentValue => $preferences->{ $option },
 &>
 % }
@@ -35,11 +35,10 @@
         my $meta = RT->Config->Meta( $option );
         my $value = $m->comp($meta->{'Widget'} .":Process",
             Arguments    => \%ARGS,
-            Empty        => 1,
+            Default      => 1,
             %{ $meta->{'WidgetArguments'} },
             Name         => $option,
-            EmptyValue   => scalar RT->Config->Get( $option ),
-            CurrentValue => $preferences->{ $option },
+            DefaultValue => scalar RT->Config->Get( $option ),
         );
         if ( defined $value ) {
             $preferences->{ $option } = $value;

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Create.html
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Create.html	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Create.html	Tue Apr 10 16:46:22 2007
@@ -87,9 +87,8 @@
 <td class="labeltop">
 <&|/l&>Cc</&>:
 </td>
-<td class="value" colspan="5">
-<input name="Cc" size="40" value="<% $ARGS{Cc} %>" /><br />
-<i><font size="-2">
+<td class="value" colspan="3"><input name="Cc" size="40" value="<% $ARGS{Cc} %>" /></td>
+<td class="comment" colspan="2"><i><font size="-2">
 <&|/l&>(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)</&></font></i>
 </td>
 </tr>
@@ -97,9 +96,8 @@
 <td class="labeltop">
 <&|/l&>Admin Cc</&>:
 </td>
-<td class="value" colspan="5">
-<input name="AdminCc" size="40" value="<% $ARGS{AdminCc} %>" /><br />
-<i><font size="-2">
+<td class="value" colspan="3"><input name="AdminCc" size="40" value="<% $ARGS{AdminCc} %>" /></td>
+<td class="comment" colspan="2"><i><font size="-2">
 <&|/l&>(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)</&></font></i>
 </td>
 </tr>
@@ -147,6 +145,15 @@
 <input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" />
 </td>
 </tr>
+
+<tr>
+<td><% loc('Sign')%></td>
+<td><& /Widgets/Form/Boolean:InputOnly, Name => 'Sign', CurrentValue => $ARGS{'Sign'} &></td>
+<td><% loc('Encrypt')%></td>
+<td><& /Widgets/Form/Boolean:InputOnly, Name => 'Encrypt', CurrentValue => $ARGS{'Encrypt'} &></td>
+<td colspan="2">&nbsp;</td>
+</tr>
+
 <tr>
 <td colspan="6">
 <&|/l&>Describe the issue below</&>:<br />
@@ -159,10 +166,6 @@
 <br />
 </td>
 </tr>
-<tr>
-<td align="right" colspan="2">
-</td>
-</tr>
 </table>
 </&>
 <& /Elements/Submit, Label => loc("Create")&>
@@ -253,6 +256,14 @@
     ARGSRef => \%ARGS
 );
 
+foreach ( qw(Sign Encrypt) ) {
+    $ARGS{ $_ } = $m->comp( '/Widgets/Form/Boolean:Process',
+        Name => $_,
+        CurrentValue => $QueueObj->$_,
+        Arguments => \%ARGS,
+    );
+}
+
 # if no due date has been set explicitly, then use the
 # queue's default if it exists
 if ($QueueObj->DefaultDueIn && !$ARGS{'Due'}) {

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Elements/ShowTransaction
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Elements/ShowTransaction	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Elements/ShowTransaction	Tue Apr 10 16:46:22 2007
@@ -113,7 +113,7 @@
 my $transdate = $Transaction->CreatedAsString();
 $transdate =~ s/\s/&nbsp;/g;
 
-my ($type, $field) = ($Transaction->Type, $Transaction->Field);
+my ($type, $field) = ($Transaction->Type, $Transaction->Field || '');
 my $type_class = $class{ $type };
 
 unless ( $type_class ) {

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Forward.html
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Forward.html	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Forward.html	Tue Apr 10 16:46:22 2007
@@ -3,6 +3,7 @@
     Ticket => $TicketObj, 
     Title  => $Title,
 &>
+<& /Elements/ListActions, actions => \@results &>
 
 <form action="Forward.html" name="ForwardMessage" method="post">
 % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS );
@@ -39,9 +40,11 @@
 Abort( loc("Couldn't load transaction #[_1]", $QuoteTransaction) )
     unless $txn->id;
 
+my @results;
 if ( $ARGS{'Forward'} ) {
     require RT::Interface::Email;
-    RT::Interface::Email::ForwardTransaction( $txn, %ARGS );
+    my ($status, $msg) = RT::Interface::Email::ForwardTransaction( $txn, %ARGS );
+    push @results, $msg;
 }
 
 my $Title = loc('Forward message');

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Boolean
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Boolean	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Boolean	Tue Apr 10 16:46:22 2007
@@ -1,26 +1,54 @@
+<%DOC>
+see docs/using_forms_widgets.pod
+</%DOC>
 <div id="form-box-<% lc $Name %>">
-<span class="description"><% $Description %></span>:
-<input type="checkbox" name="Boolean-<% $Name %>" <% $value && 'checked' %>" />
+<span class="description"><% $Description %></span>:\
+<& SELF:InputOnly, %ARGS &>
 </div>
-<%INIT>
-my $value = defined $CurrentValue ? $CurrentValue : $EmptyValue;
-</%INIT>
 <%ARGS>
 $Name         => undef,
 $Description  => undef,
-$EmptyValue   => 0,
+</%ARGS>
+
+<%METHOD InputOnly>
+<%ARGS>
+$Name         => undef,
+
+$Default        => 0,
+$DefaultValue   => 0,
+$DefaultLabel   => loc( 'Use system default ([_1])', $DefaultValue? loc('Yes'): loc('No') ),
+
 $CurrentValue => undef,
 </%ARGS>
+% unless ( $Default ) {
+<input type="hidden" name="<% $Name %>" value="0" />\
+<input type="checkbox" name="<% $Name %>" <% $CurrentValue? 'checked="checked"': '' %> />\
+% } else {
+<input type="radio" name="<% $Name %>" value="1" <% $CurrentValue? 'checked="checked"': '' %> />\
+<% loc('Yes') %>
+<input type="radio" name="<% $Name %>" value="0" <% defined $CurrentValue && !$CurrentValue? 'checked="checked"': '' %> />\
+<% loc('No') %>
+<input type="radio" name="<% $Name %>" value="__empty_value__" <% !defined $CurrentValue? 'checked="checked"': '' %> />\
+<% $DefaultLabel %>
+% }
+</%METHOD>
 
 <%METHOD Process>
 <%ARGS>
+$Name
 $Arguments    => {},
 
-$Name         => undef,
-$CurrentValue => 0,
+$Default      => 0,
+$DefaultValue => 0,
 </%ARGS>
 <%INIT>
-return $Arguments->{ "Boolean-$Name" }? 1: 0;
+my $value = $Arguments->{ $Name };
+if ( $Default ) {
+    return undef if !defined $value || $value eq '__empty_value__';
+    return $value? 1: 0;
+} else {
+    return $value? 1: 0 if defined $value;
+    return $DefaultValue;
+}
 </%INIT>
 </%METHOD>
-

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Integer
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Integer	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Integer	Tue Apr 10 16:46:22 2007
@@ -1,35 +1,48 @@
+<%DOC>
+see docs/using_forms_widgets.pod
+</%DOC>
 <div id="form-box-<% lc $Name %>">
-<span class="description"><% $Description %></span>:
-<input type="text" name="Integer-<% $Name %>" value="<% $CurrentValue || '' %>" />
-% if ( $Empty ) {
-<span class="comment"><% $EmptyLabel %></span>
+<span class="description"><% $Description %></span>:\
+<& SELF:InputOnly, %ARGS &>
+% if ( $Default ) {
+<span class="comment"><% $DefaultLabel %></span>
 % }
 </div>
 <%INIT>
 </%INIT>
 <%ARGS>
-$Name         => undef,
+$Name
+
 $Description  => undef,
 
 $CurrentValue => '',
 
-$Empty        => 0,
-$EmptyValue   => 0,
-$EmptyLabel   => loc( 'Leaving this empty will default to the system default of [_1]', $EmptyValue ),
+$Default        => 0,
+$DefaultValue   => 0,
+$DefaultLabel   => loc( 'Leaving this empty will default to the system default of [_1]', $DefaultValue ),
+</%ARGS>
+
+<%METHOD InputOnly>
+<input type="text" name="<% $Name %>" value="<% $CurrentValue || '' %>" />\
+<%ARGS>
+$Name
+$CurrentValue => '',
 </%ARGS>
+</%METHOD>
 
 <%METHOD Process>
 <%ARGS>
+$Name
+
 $Arguments    => {},
 
-$Name         => undef,
-$CurrentValue => '',
-$Empty        => 0,
+$Default      => 0,
+$DefaultValue => '',
 </%ARGS>
 <%INIT>
-my $value = $Arguments->{ "Integer-$Name" };
+my $value = $Arguments->{ $Name };
 if ( !defined $value || $value eq '' ) {
-    return $CurrentValue unless $Empty;
+    return $DefaultValue unless $Default;
     return undef;
 }
 return $value;

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Select
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Select	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Widgets/Form/Select	Tue Apr 10 16:46:22 2007
@@ -1,30 +1,59 @@
+<%DOC>
+see docs/using_forms_widgets.pod
+</%DOC>
 <div id="form-box-<% lc $Name %>">
 % if ( $Description ) {
-<% $Description %>:
+<% $Description %>:\
 % }
-<select name="<% $NamePrefix %><% $Name %>">
+<& SELF:InputOnly, %ARGS &>
+</div>
+<%ARGS>
+$Name
+$Description      => undef,
+</%ARGS>
+
+<%METHOD InputOnly>
+<%ARGS>
+$Name
+$Description      => undef,
+
+ at Values           => (),
+$ValuesCallback   => undef,
+%ValuesLabel      => (),
+ at CurrentValue     => (),
+
+$Default            => 1,
+ at DefaultValue       => (),
+$DefaultLabel       => loc('Use system default ([_1])', join ', ', @DefaultValue),
+
+$Alternative      => 0,
+$AlternativeLabel => loc('other...'),
 
-% if ( $Empty ) {
+$Multiple         => 0,
+</%ARGS>
+<select name="<% $Name %>">
+
+% if ( $Default ) {
 % my $selected = '';
-% $selected = 'selected' unless @CurrentValue;
-<option value="__empty_value__" <% $selected %>><% $EmptyLabel %></option>
+% $selected = 'selected="selected"' unless @CurrentValue;
+<option value="__empty_value__" <% $selected |n %>><% $DefaultLabel %></option>
 % }
 
 % foreach my $v( @Values ) {
 % my $selected = '';
-% $selected = 'selected' if delete $CurrentValue{ $v };
-<option value="<% $v %>" <% $selected %>><% $ValuesLabel{ $v } || $v %></option>
+% $selected = 'selected="selected"' if delete $CurrentValue{ $v };
+<option value="<% $v %>" <% $selected |n %>><% $ValuesLabel{ $v } || $v %></option>
 % }
 
 % if ( $Alternative ) {
 %     my $selected = '';
-%     $selected = 'selected' if keys %CurrentValue;
-<option value="__alternative_value__" <% $selected %>><% $AlternativeLabel %></option>
+%     $selected = 'selected="selected"' if keys %CurrentValue;
+<option value="__alternative_value__" <% $selected |n %>><% $AlternativeLabel %></option>
 % }
 
 </select>
 % if ( $Alternative ) {
-<input type="text" class="alternative" name="<% $NamePrefix %>Alternative-<% $Name %>" value="<% join ', ', @CurrentValue %>" />
+<input type="text" class="alternative" name="Alternative-<% $Name %>" value="<% join ', ', @CurrentValue %>" />
 % }
 </div>
 <%INIT>
@@ -32,7 +61,6 @@
 if ( $ValuesCallback ) {
     my $values = $ValuesCallback->(
         CurrentUser => $session{'CurrentUser'},
-        NamePrefix  => $NamePrefix,
         Name        => $Name,
     );
     if ( ref $values eq 'ARRAY' ) {
@@ -43,54 +71,34 @@
     }
 }
 </%INIT>
-<%ARGS>
-$NamePrefix       => 'Select-',
-$Name             => '',
-$Description      => undef,
-
- at Values           => (),
-$ValuesCallback   => undef,
-%ValuesLabel      => (),
- at CurrentValue     => (),
-
-$Empty            => 1,
- at EmptyValue       => (),
-$EmptyLabel       => loc('Use system default ([_1])', join ', ', @EmptyValue),
-
-$Alternative      => 0,
-$AlternativeLabel => loc('other...'),
-
-$Multiple         => 0,
-</%ARGS>
+</%METHOD>
 
 <%METHOD Process>
 <%ARGS>
-$Arguments        => {},
+$Name
 
-$NamePrefix       => 'Select-',
-$Name             => undef,
+$Arguments        => {},
 
 @Values           => (),
 %ValuesLabel      => (),
- at CurrentValue     => (),
 
-$Empty            => 1,
+$Default          => 0,
+ at DefaultValue     => (),
+
 $Alternative      => 0,
 $Multiple         => 0,
 </%ARGS>
 <%INIT>
-my $value = $Arguments->{ $NamePrefix . $Name };
+my $value = $Arguments->{ $Name };
 if( !defined $value || $value eq '__empty_value__' ) {
-    unless ( $Empty ) {
-        return [ @CurrentValue ] if $Multiple;
-        return $CurrentValue[0];
-    }
-    return undef;
+    return undef if $Default;
+    return [ @DefaultValue ] if $Multiple;
+    return $DefaultValue[0];
 }
 $value = [$value] unless ref $value;
 
 if ( $Alternative ) {
-    my $alt = $Arguments->{ $NamePrefix ."Alternative-". $Name };
+    my $alt = $Arguments->{ "Alternative-". $Name };
     if( $Multiple ) {
         push @$value, split /\s*,\s*/, $alt;
     } else {

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/SendEmail.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/SendEmail.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/SendEmail.pm	Tue Apr 10 16:46:22 2007
@@ -250,8 +250,9 @@
     }
 
     return(0) unless RT::Interface::Email::SendEmail(
-        entity => $MIMEObj,
-        transaction => $self->TransactionObj,
+        Entity => $MIMEObj,
+        Ticket => $self->TicketObj,
+        Transaction => $self->TransactionObj,
     );
 
     my $success = $msgid . " sent ";

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Condition/OwnerChange.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Condition/OwnerChange.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Condition/OwnerChange.pm	Tue Apr 10 16:46:22 2007
@@ -64,7 +64,7 @@
 
 sub IsApplicable {
     my $self = shift;
-    if ($self->TransactionObj->Field eq 'Owner') {
+    if ( ( $self->TransactionObj->Field || '' ) eq 'Owner' ) {
 	return(1);
     } 
     else {

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Crypt/GnuPG.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Crypt/GnuPG.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Crypt/GnuPG.pm	Tue Apr 10 16:46:22 2007
@@ -35,9 +35,18 @@
        verbose
 );
 
-=head2 SignEncrypt Entity => MIME::Entity, [ Encrypt => 1, Sign => 1, Passphrase => '' ]
+=head2 SignEncrypt Entity => MIME::Entity, [ Encrypt => 1, Sign => 1, Passphrase => undef ]
 
-Sign and/or encrypt email message with GnuPG.
+Signs and/or encrypts an email message with GnuPG utility. A passphrase is required
+only during signing.
+
+Returns a hash with the follwoing keys:
+
+* exit_code
+* error
+* logger
+* status
+* message
 
 =cut
 
@@ -51,6 +60,10 @@
     );
     my $entity = $args{'Entity'};
 
+    if ( $args{'Sign'} && !defined $args{'Passphrase'} ) {
+        $args{'Passphrase'} = GetPassphrase();
+    }
+
     my $gnupg = new GnuPG::Interface;
     my %opt = RT->Config->Get('GnuPG');
     $opt{'digest-algo'} ||= 'SHA1';
@@ -471,7 +484,13 @@
 }
 
 sub DecryptRFC3156 {
-    my %args = ( Data => undef, Info => undef, Top => undef, Passphrase => undef, @_ );
+    my %args = (
+        Data => undef,
+        Info => undef,
+        Top => undef,
+        Passphrase => undef,
+        @_
+    );
 
     my $gnupg = new GnuPG::Interface;
     my %opt = RT->Config->Get('GnuPG');
@@ -481,6 +500,9 @@
         meta_interactive => 0,
     );
 
+    $args{'Passphrase'} = GetPassphrase()
+        unless defined $args{'Passphrase'};
+
     my ($tmp_fh, $tmp_fn) = File::Temp::tempfile();
     binmode $tmp_fh, ':raw';
 
@@ -530,6 +552,10 @@
     return %res;
 }
 
+sub GetPassphrase {
+    return undef;
+}
+
 my %inv_recp_reason = (
     0 => "No specific reason given",
     1 => "Not Found",
@@ -683,7 +709,19 @@
             }
             foreach my $line ( @status[ $i .. $#status ] ) {
                 next unless $line =~ /^VALIDSIG\s+(.*)/;
-                @res{ qw(Fingerprint CreationDate Timestamp ExpireTimestamp Version Reserved PubkeyAlgo HashAlgo Class PKFingerprint Other) } = split /\s+/, $1, 11;
+                @res{ qw(
+                    Fingerprint
+                    CreationDate
+                    Timestamp
+                    ExpireTimestamp
+                    Version
+                    Reserved
+                    PubkeyAlgo
+                    HashAlgo
+                    Class
+                    PKFingerprint
+                    Other
+                ) } = split /\s+/, $1, 10;
                 last;
             }
             push @res, \%res;
@@ -775,6 +813,158 @@
     return %res;
 }
 
+sub GetPublicKeyInfo {
+    return GetKeyInfo(shift, 'public');
+}
+
+sub GetPrivateKeyInfo {
+    return GetKeyInfo(shift, 'private');
+}
+
+sub GetKeyInfo {
+    my $email = shift;
+    my $type = shift || 'public';
+
+    my $gnupg = new GnuPG::Interface;
+    my %opt = RT->Config->Get('GnuPG');
+    $opt{'digest-algo'} ||= 'SHA1';
+    $opt{'with-colons'} = undef; # parseable format
+    $opt{'fixed-list-mode'} = undef; # don't merge uid with keys
+    $gnupg->options->hash_init(
+        _PrepareGnuPGOptions( %opt ),
+        armor => 1,
+        meta_interactive => 0,
+    );
+
+    my %res;
+
+    my %handle;
+    my $handles = GnuPG::Handles->new(
+        stdout => ($handle{'output'} = new IO::Handle),
+        stderr => ($handle{'error'}  = new IO::Handle),
+        logger => ($handle{'logger'} = new IO::Handle),
+        status => ($handle{'status'} = new IO::Handle),
+    );
+
+    eval {
+        local $SIG{'CHLD'} = 'DEFAULT';
+        local @ENV{'LANG', 'LC_ALL'} = ('C', 'C');
+        my $method = $type eq 'private'? 'list_secret_keys': 'list_public_keys';
+        my $pid = $gnupg->$method( handles => $handles, command_args => [ $email ]  );
+        waitpid $pid, 0;
+    };
+
+    my @info = readline $handle{'output'};
+    use Data::Dumper; $RT::Logger->error( Dumper(\@info) );
+
+    close $handle{'output'};
+
+    $res{'exit_code'} = $?;
+    foreach ( qw(error logger status) ) {
+        $res{$_} = do { local $/; readline $handle{$_} };
+        delete $res{$_} unless $res{$_} && $res{$_} =~ /\S/s;
+        close $handle{$_};
+    }
+    $RT::Logger->debug( $res{'status'} ) if $res{'status'};
+    $RT::Logger->warning( $res{'error'} ) if $res{'error'};
+    $RT::Logger->error( $res{'logger'} ) if $res{'logger'} && $?;
+    if ( $@ || $? ) {
+        $res{'message'} = $@? $@: "gpg exitted with error code ". ($? >> 8);
+        return %res;
+    }
+
+    @info = ParseKeysInfo( @info );
+    $res{'info'} = $info[0];
+    return %res;
+}
+
+sub ParseKeysInfo {
+    my @lines = @_;
+
+    my @res = ();
+    foreach my $line( @lines ) {
+        chomp $line;
+        my ($tag, $line) = split /:/, $line, 2;
+        if ( $tag eq 'pub' ) {
+            my %info;
+            @info{ qw(
+                Trust KeyLenght Algorithm Key
+                Created Expire Empty OwnerTrust
+                Empty Empty KeyCapabilities Other
+            ) } = split /:/, $line, 12;
+            $info{'Trust'} = _ConvertTrustChar( $info{'Trust'} );
+            $info{'OwnerTrust'} = _ConvertTrustChar( $info{'OwnerTrust'} );
+            $info{ $_ } = _ParseDate( $info{ $_ } )
+                foreach qw(Created Expire);
+            push @res, \%info;
+        }
+        elsif ( $tag eq 'sec' ) {
+            my %info;
+            @info{ qw(
+                Empty KeyLenght Algorithm Key
+                Created Expire Empty OwnerTrust
+                Empty Empty KeyCapabilities Other
+            ) } = split /:/, $line, 12;
+            $info{'OwnerTrust'} = _ConvertTrustChar( $info{'OwnerTrust'} );
+            $info{ $_ } = _ParseDate( $info{ $_ } )
+                foreach qw(Created Expire);
+            push @res, \%info;
+        }
+        elsif ( $tag eq 'uid' ) {
+            my %info;
+            @info{ qw(Trust Created Expire String) }
+                = (split /:/, $line)[0,4,5,8];
+            $info{ $_ } = _ParseDate( $info{ $_ } )
+                foreach qw(Created Expire);
+            push @{ $res[-1]{'User'} ||= [] }, \%info;
+        }
+        elsif ( $tag eq 'fpr' ) {
+            $res[-1]{'Fingerprint'} = (split /:/, $line, 10)[8];
+        }
+    }
+    return @res;
+}
+
+{
+    my %mapping = (
+        o => 'Unknown (this value is new to the system)', #loc
+        # deprecated
+        d   => "The key has been disabled", #loc
+        r   => "The key has been revoked", #loc
+        e   => "The key has expired", #loc
+        '-' => 'Unknown (no trust value assigned)', #loc
+        #gpupg docs says that '-' and 'q' may safely be treated as the same value
+        q   => 'Unknown (no trust value assigned)', #loc
+        n   => "Don't trust this key at all", #loc
+        m   => "There is marginal trust in this key", #loc
+        f   => "The key is fully trusted", #loc
+        u   => "The key is ultimately trusted", #loc
+    );
+    sub _ConvertTrustChar {
+        my $value = shift;
+        return $mapping{'-'} unless $value;
+
+        $value = substr $value, 0, 1;
+        return $mapping{ $value } || $mapping{'o'};
+    }
+}
+
+sub _ParseDate {
+    my $value = shift;
+    # never
+    return $value unless $value;
+
+    require RT::Date;
+    my $obj = RT::Date->new( $RT::SystemUser );
+    # unix time
+    if ( $value =~ /^\d+$/ ) {
+        $obj->Set( Value => $value );
+    } else {
+        $obj->Set( Format => 'unknown', Value => $value, Timezone => 'utc' );
+    }
+    return $obj;
+}
+
 1;
 
 # helper package to avoid using temp file

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Email.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Email.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Email.pm	Tue Apr 10 16:46:22 2007
@@ -99,8 +99,9 @@
 
 =head2 CheckForLoops HEAD
 
-Returns true if the message's been sent by this RT instance.
-Uses "X-RT-Loop-Prevention" field of the head for that.
+Takes a HEAD object of L<MIME::Head> class and returns true if the
+message's been sent by this RT instance. Uses "X-RT-Loop-Prevention"
+field of the head for test.
 
 =cut
 
@@ -121,7 +122,8 @@
 
 =head2 CheckForSuspiciousSender HEAD
 
-Returns true if sender is suspicious. Suspicious means mailer daemon.
+Takes a HEAD object of L<MIME::Head> class and returns true if sender
+is suspicious. Suspicious means mailer daemon.
 
 See also L</ParseSenderAddressFromHead>.
 
@@ -151,8 +153,9 @@
 
 =head2 CheckForAutoGenerated HEAD
 
-Returns true if message is autogenerated. Checks 'Precedence'
-and 'X-FC-Machinegenerated' fields of the head.
+Takes a HEAD object of L<MIME::Head> class and returns true if message
+is autogenerated. Checks 'Precedence' and 'X-FC-Machinegenerated'
+fields of the head in tests.
 
 =cut
 
@@ -262,6 +265,7 @@
         level   => $args{'LogLevel'},
         message => $args{'Explanation'}
     ) if $args{'LogLevel'};
+
     my $entity = MIME::Entity->build(
         Type                   => "multipart/mixed",
         From                   => $args{'From'},
@@ -269,7 +273,7 @@
         To                     => $args{'To'},
         Subject                => $args{'Subject'},
         Precedence             => 'bulk',
-        'X-RT-Loop-Prevention' => RT->Config->Get('rtname'),
+        'X-RT-Loop-Prevention' => scalar RT->Config->Get('rtname'),
         'In-Reply-To:'         => $args{'MIMEObj'} ? $args{'MIMEObj'}->head->get('Message-Id') : undef,
     );
 
@@ -285,38 +289,68 @@
 
     }
 
-    SendEmail(entity => $entity, bounce => 1);
+    SendEmail( Entity => $entity, Bounce => 1 );
 }
 
 
-=head2 SendEmail entity => ENTITY, [bounce => BOUNCE]
+=head2 SendEmail Entity => undef, [ Bounce => 0, Ticket => undef, Transaction => undef ]
 
 Sends an email (passed as a L<MIME::Entity> object C<ENTITY>) using
-RT's outgoing mail configuration.  If C<BOUNCE> is passed, and is a
+RT's outgoing mail configuration. If C<BOUNCE> is passed, and is a
 true value, the message will be marked as an autogenerated error, if
 possible.
 
+Sets Date field of the head to now if it's not set.
+
 =cut
 
 sub SendEmail {
     my (%args) = (
-        entity => undef,
-        bounce => 0,
+        Entity => undef,
+        Bounce => 0,
+        Ticket => undef,
+        Transaction => undef,
         @_,
     );
-    unless ( $args{'entity'} ) {
-        $RT::Logger->crit( "Could not send mail without 'entity' object" );
+    foreach my $arg( qw(Entity Bounce) ) {
+        next unless defined $args{ lc $arg };
+
+        $RT::Logger->warning("'". lc($arg) ."' argument is deprecated, use '$arg' instead");
+        $args{ $arg } = delete $args{ lc $arg };
+    }
+
+    unless ( $args{'Entity'} ) {
+        $RT::Logger->crit( "Could not send mail without 'Entity' object" );
         return 0;
     }
 
-    my $msgid = $args{'entity'}->head->get('Message-ID') || '';
+    if ( $args{'Transaction'} && !$args{'Ticket'}
+        && $args{'Transaction'}->ObjectType eq 'RT::Ticket' )
+    {
+        $args{'Ticket'} = $args{'Transaction'}->Object;
+    }
+
+    if ( $args{'Ticket'} ) {
+        my $sign = $args{'Ticket'}->QueueObj->Sign;
+        my $encrypt = $args{'Ticket'}->QueueObj->Encrypt;
+        if ( $sign || $encrypt ) {
+            require RT::Crypt::GnuPG;
+            my %res = RT::Crypt::GnuPG::SignEncrypt(
+                Entity => $args{'Entity'},
+                Sign => $sign, Encrypt => $encrypt,
+            );
+            return 0 if $res{'exit_code'};
+        }
+    }
+
+    my $msgid = $args{'Entity'}->head->get('Message-ID') || '';
     chomp $msgid;
 
-    unless ( $args{'entity'}->head->get('Date') ) {
+    unless ( $args{'Entity'}->head->get('Date') ) {
         require RT::Date;
         my $date = RT::Date->new( $RT::SystemUser );
         $date->SetToNow;
-        $args{'entity'}->head->set( 'Date', $date->RFC2822( Timezone => 'server' ) );
+        $args{'Entity'}->head->set( 'Date', $date->RFC2822( Timezone => 'server' ) );
     }
 
     my $mail_command = RT->Config->Get('MailCommand');
@@ -324,14 +358,14 @@
     if ( $mail_command eq 'sendmailpipe' ) {
         my $path = RT->Config->Get('SendmailPath');
         my $args = RT->Config->Get('SendmailArguments');
-        $args .= ' '. RT->Config->Get('SendmailBounceArguments') if $args{'bounce'};
+        $args .= ' '. RT->Config->Get('SendmailBounceArguments') if $args{'Bounce'};
 
         # VERP
-        if ( $args{'transaction'} and
+        if ( $args{'Transaction'} and
              my $prefix = RT->Config->Get('VERPPrefix') and
              my $domain = RT->Config->Get('VERPDomain') )
         {
-            my $from = $args{'transaction'}->CreatorObj->EmailAddress;
+            my $from = $args{'Transaction'}->CreatorObj->EmailAddress;
             $from =~ s/@/=/g;
             $from =~ s/\s//g;
             $args .= " -f $prefix$from\@$domain";
@@ -345,7 +379,7 @@
 
             # if something wrong with $mail->print we will get PIPE signal, handle it
             local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
-            $args{'entity'}->print($mail);
+            $args{'Entity'}->print($mail);
 
             unless ( close $mail ) {
                 die "close pipe failed: $!" if $!; # system error
@@ -370,7 +404,7 @@
             push @mailer_args, split(/\s+/, RT->Config->Get('SendmailArguments'));
         }
         elsif ( $mail_command eq 'smtp' ) {
-            $ENV{'MAILADDRESS'} = RT->Config->Get('SMTPFrom') || $args{'entity'}->head->get('From');
+            $ENV{'MAILADDRESS'} = RT->Config->Get('SMTPFrom') || $args{'Entity'}->head->get('From');
             push @mailer_args, ( Server => RT->Config->Get('SMTPServer') );
             push @mailer_args, ( Debug  => RT->Config->Get('SMTPDebug') );
         }
@@ -378,7 +412,7 @@
             push @mailer_args, RT->Config->Get('MailParams');
         }
 
-        unless ( $args{'entity'}->send( @mailer_args ) ) {
+        unless ( $args{'Entity'}->send( @mailer_args ) ) {
             $RT::Logger->crit( "$msgid: Could not send mail." );
             return 0;
         }
@@ -441,7 +475,9 @@
         Encoding => '8bit',
         Data => $entity->as_string,
     );
-    SendEmail( entity => $mail );
+    my $status = SendEmail( Entity => $mail, Transaction => $txn );
+    return (0, $txn->loc("Couldn't send email")) unless $status;
+    return (1, $txn->loc("Send email successfully"));
 }
 
 sub CreateUser {

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Ticket_Overlay.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Ticket_Overlay.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Ticket_Overlay.pm	Tue Apr 10 16:46:22 2007
@@ -2171,44 +2171,42 @@
 =cut
 
 sub _RecordNote {
-
     my $self = shift;
-    my %args = ( CcMessageTo  => undef,
-                 BccMessageTo => undef,
-                 MIMEObj      => undef,
-                 Content      => undef,
-                 TimeTaken    => 0,
-                 CommitScrips => 1,
-                 @_ );
+    my %args = ( 
+        CcMessageTo  => undef,
+        BccMessageTo => undef,
+        MIMEObj      => undef,
+        Content      => undef,
+        TimeTaken    => 0,
+        CommitScrips => 1,
+        @_
+    );
 
     unless ( $args{'MIMEObj'} || $args{'Content'} ) {
-            return ( 0, $self->loc("No message attached"), undef );
+        return ( 0, $self->loc("No message attached"), undef );
     }
+
     unless ( $args{'MIMEObj'} ) {
-            $args{'MIMEObj'} = MIME::Entity->build( Data => (
-                                                          ref $args{'Content'}
-                                                          ? $args{'Content'}
-                                                          : [ $args{'Content'} ]
-                                                    ) );
-        }
+        $args{'MIMEObj'} = MIME::Entity->build(
+            Data => ( ref $args{'Content'}? $args{'Content'}: [ $args{'Content'} ] )
+        );
+    }
 
     # convert text parts into utf-8
     RT::I18N::SetMIMEEntityToUTF8( $args{'MIMEObj'} );
 
-# If we've been passed in CcMessageTo and BccMessageTo fields,
-# add them to the mime object for passing on to the transaction handler
-# The "NotifyOtherRecipients" scripAction will look for RT-Send-Cc: and RT-Send-Bcc:
-# headers
-
-    $args{'MIMEObj'}->head->add( 'RT-Send-Cc', RT::User::CanonicalizeEmailAddress(
-                                                     undef, $args{'CcMessageTo'}
-                                 ) )
-      if defined $args{'CcMessageTo'};
-    $args{'MIMEObj'}->head->add( 'RT-Send-Bcc',
-                                 RT::User::CanonicalizeEmailAddress(
-                                                    undef, $args{'BccMessageTo'}
-                                 ) )
-      if defined $args{'BccMessageTo'};
+    # If we've been passed in CcMessageTo and BccMessageTo fields,
+    # add them to the mime object for passing on to the transaction handler
+    # The "NotifyOtherRecipients" scripAction will look for RT-Send-Cc: and
+    # RT-Send-Bcc: headers
+
+    $args{'MIMEObj'}->head->add(
+        'RT-Send-Cc' => RT::User->CanonicalizeEmailAddress( $args{'CcMessageTo'} )
+    ) if defined $args{'CcMessageTo'};
+
+    $args{'MIMEObj'}->head->add(
+        'RT-Send-Bcc' => RT::User->CanonicalizeEmailAddress( $args{'BccMessageTo'} )
+    ) if defined $args{'BccMessageTo'};
 
     # XXX: This code is duplicated several times
     # If this is from an external source, we need to come up with its

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Tickets_Overlay.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Tickets_Overlay.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Tickets_Overlay.pm	Tue Apr 10 16:46:22 2007
@@ -593,8 +593,20 @@
 
     # See the comments for TransLimit, they apply here too
 
-    $sb->{_sql_transalias} = $sb->NewAlias('Transactions')
-        unless defined $sb->{_sql_transalias};
+    unless ( $sb->{_sql_transalias} ) {
+        $sb->{_sql_transalias} = $sb->Join(
+            ALIAS1 => 'main',
+            FIELD1 => 'id',
+            TABLE2 => 'Transactions',
+            FIELD2 => 'ObjectId',
+        );
+        $sb->SUPER::Limit(
+            ALIAS           => $sb->{_sql_transalias},
+            FIELD           => 'ObjectType',
+            VALUE           => 'RT::Ticket',
+            ENTRYAGGREGATOR => 'AND',
+        );
+    }
 
     my $date = RT::Date->new( $sb->CurrentUser );
     $date->Set( Format => 'unknown', Value => $value );
@@ -645,20 +657,6 @@
         );
     }
 
-    # Join Transactions to Tickets
-    $sb->_SQLJoin(
-        ALIAS1 => 'main',
-        FIELD1 => $sb->{'primary_key'},     # UGH!
-        ALIAS2 => $sb->{_sql_transalias},
-        FIELD2 => 'ObjectId'
-    );
-
-    $sb->SUPER::Limit(
-        ALIAS => $sb->{_sql_transalias},
-        FIELD => 'ObjectType',
-        VALUE => 'RT::Ticket'
-    );
-
     $sb->_CloseParen;
 }
 
@@ -707,10 +705,29 @@
 
     my ( $self, $field, $op, $value, @rest ) = @_;
 
-    $self->{_sql_transalias} = $self->NewAlias('Transactions')
-        unless defined $self->{_sql_transalias};
-    $self->{_sql_trattachalias} = $self->NewAlias('Attachments')
-        unless defined $self->{_sql_trattachalias};
+    unless ( $self->{_sql_transalias} ) {
+        $self->{_sql_transalias} = $self->Join(
+            ALIAS1 => 'main',
+            FIELD1 => 'id',
+            TABLE2 => 'Transactions',
+            FIELD2 => 'ObjectId',
+        );
+        $self->SUPER::Limit(
+            ALIAS           => $self->{_sql_transalias},
+            FIELD           => 'ObjectType',
+            VALUE           => 'RT::Ticket',
+            ENTRYAGGREGATOR => 'AND',
+        );
+    }
+    unless ( defined $self->{_sql_trattachalias} ) {
+        $self->{_sql_trattachalias} = $self->_SQLJoin(
+            TYPE   => 'LEFT', # not all txns have an attachment
+            ALIAS1 => $self->{_sql_transalias},
+            FIELD1 => 'id',
+            TABLE2 => 'Attachments',
+            FIELD2 => 'TransactionId',
+        );
+    }
 
     $self->_OpenParen;
 
@@ -746,28 +763,6 @@
         );
     }
 
-    $self->_SQLJoin(
-        ALIAS1 => $self->{_sql_trattachalias},
-        FIELD1 => 'TransactionId',
-        ALIAS2 => $self->{_sql_transalias},
-        FIELD2 => 'id'
-    );
-
-    # Join Transactions to Tickets
-    $self->_SQLJoin(
-        ALIAS1 => 'main',
-        FIELD1 => $self->{'primary_key'},     # Why not use "id" here?
-        ALIAS2 => $self->{_sql_transalias},
-        FIELD2 => 'ObjectId'
-    );
-
-    $self->SUPER::Limit(
-        ALIAS           => $self->{_sql_transalias},
-        FIELD           => 'ObjectType',
-        VALUE           => 'RT::Ticket',
-        ENTRYAGGREGATOR => 'AND'
-    );
-
     $self->_CloseParen;
 
 }


More information about the Rt-commit mailing list