[Bps-public-commit] rt-extension-ajaxpreviewscrips branch, master, updated. 0724b5e5bd6acc511de4f6a1042c38b5b7cdd9d3

Alex Vandiver alexmv at bestpractical.com
Mon Jul 14 16:45:38 EDT 2014


The branch, master has been updated
       via  0724b5e5bd6acc511de4f6a1042c38b5b7cdd9d3 (commit)
       via  451a37dc6bc2754b877be859c968f5a150c28d54 (commit)
       via  3d323302af9dacd3c363e8bfa6c5e9147a809083 (commit)
      from  9180535e745e818f6c164da0b43712925f58e9ab (commit)

Summary of changes:
 .../AjaxPreviewScrips/Ticket/Update.html/AfterForm |  28 ++
 html/Elements/Crypt/SignEncryptWidget              | 190 +++++++++++++
 html/Helpers/PreviewScrips                         |  32 ++-
 html/Helpers/ShowSimplifiedRecipients              |  54 ++--
 html/Ticket/Elements/PreviewScrips                 |  24 --
 html/Ticket/Elements/ShowSimplifiedRecipients      |  27 --
 html/Ticket/ModifyPeople.html                      | 149 ++++++++++
 lib/RT/Extension/AjaxPreviewScrips.pm              | 310 +++++++++++++++++++++
 8 files changed, 731 insertions(+), 83 deletions(-)
 create mode 100644 html/Callbacks/AjaxPreviewScrips/Ticket/Update.html/AfterForm
 create mode 100644 html/Elements/Crypt/SignEncryptWidget
 create mode 100644 html/Ticket/ModifyPeople.html

- Log -----------------------------------------------------------------
commit 3d323302af9dacd3c363e8bfa6c5e9147a809083
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Mon Jul 14 16:26:34 2014 -0400

    Don't show checkboxes for one-time Ccs unless they are actually squelchable

diff --git a/html/Helpers/ShowSimplifiedRecipients b/html/Helpers/ShowSimplifiedRecipients
index ab45134..fd110f6 100644
--- a/html/Helpers/ShowSimplifiedRecipients
+++ b/html/Helpers/ShowSimplifiedRecipients
@@ -65,16 +65,18 @@ my $Object = $TicketObj->DryRun(%ARGS);
 $m->abort unless $Object;
 
 my %headers = (To => {}, Cc => {}, Bcc => {});
-my %no_squelch;
+my %no_squelch = (To => {}, Cc => {}, Bcc => {});
 if ($Object->Scrips) {
     for my $scrip (grep $_->ActionObj->Action->isa('RT::Action::SendEmail'), @{$Object->Scrips->Prepared}) {
         my $action = $scrip->ActionObj->Action;
         for my $type (qw(To Cc Bcc)) {
-            $headers{$type}{$_->address} = $_
-                for $action->$type();
-        }
-        for my $type ( keys %{$action->{NoSquelch}} ) {
-            $no_squelch{$type}{$_} ||= 1 for @{$action->{NoSquelch}{$type}};
+            for my $addr ($action->$type()) {
+                if (grep {$addr->address eq $_} @{$action->{NoSquelch}{$type} || []}) {
+                    $no_squelch{$type}{$addr->address} = $addr;
+                } else {
+                    $headers{$type}{$addr->address} = $addr;
+                }
+            }
         }
     }
 }
@@ -91,7 +93,7 @@ my %squelched = ProcessTransactionSquelching( \%ARGS );
 </%init>
 <table>
 % for my $type (qw(To Cc Bcc)) {
-%     next unless keys %{$headers{$type}};
+%     next unless keys %{$headers{$type}} or keys %{$no_squelch{$type}};
 <tr>
 <td valign="top"><% $type %>:</td>
 <td valign="top">
@@ -103,16 +105,15 @@ my %squelched = ProcessTransactionSquelching( \%ARGS );
 <label for="TxnSendMailTo-<% $addr->address %>-<% $recips{$addr->address} %>"><& /Elements/ShowUser, Address => $addr &></label>
 %         $m->callback(CallbackName => 'AfterAddress', Ticket => $TicketObj, Address => $addr, Type => $type);
 <br />
-%         if ( $no_squelch{$type}{$addr} ) {
+%     }
+%     for my $addr (sort {$a->address cmp $b->address} values %{$no_squelch{$type}}) {
 <label><& /Elements/ShowUser, Address => $addr &></label>
-%             if ( $type eq 'Cc' ) {
+%         if ( $type eq 'Cc' ) {
 (<&|/l&>explicit one-time Cc</&>)
-%             }
-%             else {
+%         } else {
 (<&|/l&>explicit one-time Bcc</&>)
-%             }
-<br />
 %         }
+<br />
 %     }
 </td></tr>
 % }

commit 451a37dc6bc2754b877be859c968f5a150c28d54
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Mon Jul 14 16:27:31 2014 -0400

    Unify recipients and previews javascript

diff --git a/html/Callbacks/AjaxPreviewScrips/Ticket/Update.html/AfterForm b/html/Callbacks/AjaxPreviewScrips/Ticket/Update.html/AfterForm
new file mode 100644
index 0000000..4c6c0af
--- /dev/null
+++ b/html/Callbacks/AjaxPreviewScrips/Ticket/Update.html/AfterForm
@@ -0,0 +1,28 @@
+<%args>
+$Ticket
+</%args>
+<%init>
+return unless $Ticket->CurrentUserHasRight('ShowOutgoingEmail')
+  or RT->Config->Get('SimplifiedRecipients', $session{'CurrentUser'});
+</%init>
+<script type="text/javascript">
+jQuery( function() {
+   var updateScrips = function() {
+       CKEDITOR.instances['UpdateContent'].updateElement();
+       var syncCheckboxes = function(ev) {
+           jQuery("input[name=TxnSendMailTo]").filter( function() { return this.value == ev.target.value; } ).prop("checked",jQuery(ev.target).prop('checked'));
+       };
+       jQuery('#recipients div.titlebox-content').load( '<% RT->Config->Get('WebPath')%>/Helpers/ShowSimplifiedRecipients',
+           jQuery('form[name=TicketUpdate]').serialize(),
+           function() { jQuery("#recipients input[name=TxnSendMailTo]").change( syncCheckboxes ); }
+       );
+       jQuery('#previewscrips div.titlebox-content').load( '<% RT->Config->Get('WebPath')%>/Helpers/PreviewScrips',
+           jQuery('form[name=TicketUpdate]').serialize(),
+           function() { jQuery("#previewscrips input[name=TxnSendMailTo]").change( syncCheckboxes ); }
+       );
+   };
+   updateScrips();
+   CKEDITOR.instances['UpdateContent'].on('blur', updateScrips );
+   jQuery("#ticket-update-metadata :input, input[name^=UpdateCc], input[name^=UpdateBcc]").change( updateScrips );
+});
+</script>
diff --git a/html/Ticket/Elements/PreviewScrips b/html/Ticket/Elements/PreviewScrips
index d7e5aa1..8ccb064 100644
--- a/html/Ticket/Elements/PreviewScrips
+++ b/html/Ticket/Elements/PreviewScrips
@@ -48,27 +48,3 @@
 <%args>
 $TicketObj => undef
 </%args>
-<script type="text/javascript">
-jQuery( function() {
-   jQuery('#ticket-update-metadata :input, input[name^=UpdateCc], input[name^=UpdateBcc]').change( function() {
-       jQuery('#previewscrips div.titlebox-content').load( '<% RT->Config->Get('WebPath')%>/Helpers/PreviewScrips',
-           jQuery('form[name=TicketUpdate]').serialize(),
-           function() {
-               jQuery("input[name=TxnSendMailTo]").change(function(ev) {
-                   jQuery("input[name=TxnSendMailTo]").filter( function() { return this.value == ev.target.value; } ).prop("checked",jQuery(ev.target).prop('checked'));
-               });
-           }
-       );
-   });
-   jQuery('#UpdateType').change();
-
-   jQuery('input[name=UpdateCc], input[name=UpdateBcc]').each( function() {
-       var old_select = jQuery(this).autocomplete('option', 'select');
-       jQuery(this).autocomplete('option', 'select', function(event, ui) {
-           var ret = old_select.call(this, event, ui);
-           jQuery(this).change();
-           return ret;
-       });
-   });
-});
-</script>
diff --git a/html/Ticket/Elements/ShowSimplifiedRecipients b/html/Ticket/Elements/ShowSimplifiedRecipients
index 2e6093b..a4cec73 100644
--- a/html/Ticket/Elements/ShowSimplifiedRecipients
+++ b/html/Ticket/Elements/ShowSimplifiedRecipients
@@ -51,32 +51,5 @@ $TicketObj
 <%INIT>
 return unless RT->Config->Get('SimplifiedRecipients', $session{'CurrentUser'});
 </%INIT>
-<script type="text/javascript">
-jQuery( function() {
-   jQuery('#ticket-update-metadata :input, input[name^=UpdateCc], input[name^=UpdateBcc]').change( function() {
-       jQuery('#recipients div.titlebox-content').load( '<% RT->Config->Get('WebPath')%>/Helpers/ShowSimplifiedRecipients',
-           jQuery('form[name=TicketUpdate]').serialize(),
-           function() {
-% unless ($TicketObj->CurrentUserHasRight('ShowOutgoingEmail')) {
-               jQuery("input[name=TxnSendMailTo]").change(function(ev) {
-                   jQuery("input[name=TxnSendMailTo]").filter( function() { return this.value == ev.target.value; } ).prop("checked",jQuery(ev.target).prop('checked'));
-               });
-% }
-        });
-   });
-% unless ($TicketObj->CurrentUserHasRight('ShowOutgoingEmail')) {
-   jQuery('#UpdateType').change();
-   jQuery('input[name=UpdateCc], input[name=UpdateBcc]').each( function() {
-       var old_select = jQuery(this).autocomplete('option', 'select');
-       jQuery(this).autocomplete('option', 'select', function(event, ui) {
-           var ret = old_select.call(this, event, ui);
-           jQuery(this).change();
-           return ret;
-       });
-   } );
-% }
-});
-</script>
-
 <&|/Widgets/TitleBox, title => loc('Recipients'), id => 'recipients' &>
 </&>

commit 0724b5e5bd6acc511de4f6a1042c38b5b7cdd9d3
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Mon Jul 14 16:27:50 2014 -0400

    Preview TransactionBatch scrips

diff --git a/html/Elements/Crypt/SignEncryptWidget b/html/Elements/Crypt/SignEncryptWidget
new file mode 100644
index 0000000..165210c
--- /dev/null
+++ b/html/Elements/Crypt/SignEncryptWidget
@@ -0,0 +1,190 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<table><tr>
+% my $columnsplit = "</td><td>";
+% if ( RT->Config->Get('Crypt')->{'Outgoing'} eq 'GnuPG' ) {
+<td><% loc( 'Sign[_1][_2] using [_3]',
+    $columnsplit,
+    $m->scomp('/Widgets/Form/Boolean:InputOnly',
+        Name => 'Sign', CurrentValue => $self->{'Sign'}
+    ),
+    $m->scomp('SelectKeyForSigning', User => $session{'CurrentUser'}->UserObj ),
+) |n %></td>
+% } else {
+<td><% loc( 'Sign[_1][_2]',
+    $columnsplit,
+    $m->scomp('/Widgets/Form/Boolean:InputOnly',
+        Name => 'Sign', CurrentValue => $self->{'Sign'}
+    ),
+) |n %></td>
+% }
+
+<td><% loc('Encrypt')%></td>
+<td><& /Widgets/Form/Boolean:InputOnly, Name => 'Encrypt', CurrentValue => $self->{'Encrypt'} &></td>
+</tr></table>
+<%ARGS>
+$self => undef,
+</%ARGS>
+<%INIT>
+return unless $self;
+</%INIT>
+
+<%METHOD new>
+<%ARGS>
+$Arguments => {}
+</%ARGS>
+<%INIT>
+return undef unless RT->Config->Get('Crypt')->{'Enable'};
+return { %$Arguments };
+</%INIT>
+</%METHOD>
+
+<%METHOD ShowIssues>
+<%ARGS>
+$self => undef,
+</%ARGS>
+<%INIT>
+return unless $self;
+
+return $m->comp( '/Elements/Crypt/KeyIssues',
+    Issues => $self->{'GnuPGRecipientsKeyIssues'} || [],
+    SignAddresses => $self->{'GnuPGCanNotSignAs'} || [],
+);
+</%INIT>
+</%METHOD>
+
+
+<%METHOD Process>
+<%ARGS>
+$self => undef
+$QueueObj => undef
+$TicketObj => undef
+</%ARGS>
+<%INIT>
+return unless $self;
+
+$QueueObj ||= $TicketObj->QueueObj if $TicketObj;
+
+foreach ( qw(Sign Encrypt) ) {
+    $self->{ $_ } = $m->comp( '/Widgets/Form/Boolean:Process',
+        Name => $_,
+        DefaultValue => $QueueObj->$_,
+        Arguments => $self,
+    );
+}
+</%INIT>
+</%METHOD>
+
+<%METHOD Check>
+<%ARGS>
+$self      => undef
+$Operation => 'Update'
+$TicketObj => undef
+$QueueObj  => undef
+</%ARGS>
+<%INIT>
+return 1 unless $self;
+
+my $checks_failure = 0;
+
+if ( $self->{'Sign'} ) {
+    $QueueObj ||= $TicketObj->QueueObj
+        if $TicketObj;
+
+    my $private = $session{'CurrentUser'}->UserObj->PrivateKey || '';
+    my $queue = ($self->{'UpdateType'} && $self->{'UpdateType'} eq "private")
+        ? ( $QueueObj->CommentAddress || RT->Config->Get('CommentAddress') )
+        : ( $QueueObj->CorrespondAddress || RT->Config->Get('CorrespondAddress') );
+
+    my $address = $self->{'SignUsing'} || $queue;
+    if ($address ne $private and $address ne $queue) {
+        push @{ $self->{'GnuPGCanNotSignAs'} ||= [] }, $address;
+        $checks_failure = 1;
+    } elsif ( not RT::Crypt->DrySign( Signer => $address ) ) {
+        push @{ $self->{'GnuPGCanNotSignAs'} ||= [] }, $address;
+        $checks_failure = 1;
+    } else {
+        RT::Crypt->UseKeyForSigning( $self->{'SignUsing'} )
+            if $self->{'SignUsing'};
+    }
+}
+
+if ( $self->{'Encrypt'} ) {
+
+    my @recipients;
+
+    if ( $Operation eq 'Update' ) {
+        @recipients = map {$_->Recipients} $TicketObj->DryRun(
+            sub { ProcessUpdateMessage( ARGSRef => {%$self}, TicketObj => $TicketObj ) } );
+    }
+    elsif ( $Operation eq 'Create' ) {
+        $TicketObj = RT::Ticket->new( $session{'CurrentUser'} );
+        @recipients = map {$_->Recipients} $TicketObj->DryRun(
+            sub { CreateTicket( %$self, TicketObj => $TicketObj ); } );
+    }
+    else {
+        $RT::Logger->crit('Incorrect operation: '. $Operation );
+    }
+
+    my %seen;
+    @recipients = grep !$seen{ lc $_ }++, @recipients;
+
+    RT::Crypt->UseKeyForEncryption(
+        map { (/^UseKey-(.*)$/)[0] => $self->{ $_ } }
+        grep $self->{ $_ } && /^UseKey-/,
+        keys %$self
+    );
+
+    my ($status, @issues) = RT::Crypt->CheckRecipients( @recipients );
+    push @{ $self->{'GnuPGRecipientsKeyIssues'} ||= [] }, @issues;
+    $checks_failure = 1 unless $status;
+}
+
+return $checks_failure ? 0 : 1;
+</%INIT>
+</%METHOD>
diff --git a/html/Helpers/PreviewScrips b/html/Helpers/PreviewScrips
index 8cceb89..ef7db09 100644
--- a/html/Helpers/PreviewScrips
+++ b/html/Helpers/PreviewScrips
@@ -60,9 +60,20 @@ else {
       unless $TicketObj->CurrentUserHasRight( 'ReplyToTicket' ) || $TicketObj->CurrentUserHasRight( 'ModifyTicket' );
 }
 
-my $Object = $TicketObj->DryRun(%ARGS);
+my @dryrun = $TicketObj->DryRun(
+    sub {
+        local $ARGS{UpdateContent} ||= "Content";
+        ProcessUpdateMessage(ARGSRef  => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketWatchers(ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketBasics(  ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketLinks(   ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketDates(   ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessObjectCustomFieldUpdates(ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketReminders( ARGSRef => \%ARGS, TicketObj => $TicketObj );
+    }
+);
 my %recips;
-$m->abort unless $Object;
+$m->abort unless @dryrun;
 
 my %squelched = ProcessTransactionSquelching( \%ARGS );
 </%init>
@@ -71,13 +82,9 @@ my %squelched = ProcessTransactionSquelching( \%ARGS );
 &>Uncheck boxes to disable notifications to the listed recipients <b>for this transaction only</b>; persistent squelching is managed on the <a href="[_1]">People page</a>.</&>
 </p>
 
-% if ( $Object->Scrips ) {
-%     # Sort scrips with recipients before those without
-%     my @scrips = map { $_->[0] }
-%                  sort { ($b->[1]?1:0) <=> ($a->[1]?1:0) }
-%                  map { [$_, $_->ActionObj->Action->To + $_->ActionObj->Action->Cc + $_->ActionObj->Action->Bcc] }
-%                  grep {$_->ActionObj->Action->isa('RT::Action::SendEmail')}
-%                  @{$Object->Scrips->Prepared};
+% my @scrips = grep {$_->ActionObj->Action->isa('RT::Action::SendEmail')}
+%              map {@{$_->Scrips->Prepared}} @dryrun;
+% if (@scrips) {
 %     for my $scrip (@scrips) {
           <b><% $scrip->Description || loc('Scrip #[_1]',$scrip->id) %></b><br />
           <&|/l, loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->Template)&>[_1] [_2] with template [_3]</&>
@@ -123,8 +130,9 @@ my %squelched = ProcessTransactionSquelching( \%ARGS );
 %     }
 % }
 
-% if ( $Object->Rules ) {
-%     for my $rule (@{$Object->Rules}) {
+% my @rules = map {@{$_->Rules}} @dryrun;
+% if ( @rules ) {
+%     for my $rule (@rules) {
 %         next unless $rule->{hints} && $rule->{hints}{class} eq 'SendEmail';
           <b><% $rule->Describe %></b>
 %         my $data = $rule->{hints}{recipients};
@@ -150,4 +158,4 @@ my %squelched = ProcessTransactionSquelching( \%ARGS );
 % $m->callback( CallbackName => 'AfterRecipients', TicketObj => $TicketObj );
 
 <input type="hidden" name="TxnRecipients" value="<% join ",",sort keys %recips %>" />
-% $m->abort();
\ No newline at end of file
+% $m->abort();
diff --git a/html/Helpers/ShowSimplifiedRecipients b/html/Helpers/ShowSimplifiedRecipients
index fd110f6..68e86cc 100644
--- a/html/Helpers/ShowSimplifiedRecipients
+++ b/html/Helpers/ShowSimplifiedRecipients
@@ -61,13 +61,25 @@ else {
       unless $TicketObj->CurrentUserHasRight( 'ReplyToTicket' ) || $TicketObj->CurrentUserHasRight( 'ModifyTicket' );
 }
 
-my $Object = $TicketObj->DryRun(%ARGS);
-$m->abort unless $Object;
+my @dryrun = $TicketObj->DryRun(
+    sub {
+        local $ARGS{UpdateContent} ||= "Content";
+        ProcessUpdateMessage(ARGSRef  => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketWatchers(ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketBasics(  ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketLinks(   ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketDates(   ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessObjectCustomFieldUpdates(ARGSRef => \%ARGS, TicketObj => $TicketObj );
+        ProcessTicketReminders( ARGSRef => \%ARGS, TicketObj => $TicketObj );
+    }
+);
+$m->abort unless @dryrun;
 
 my %headers = (To => {}, Cc => {}, Bcc => {});
 my %no_squelch = (To => {}, Cc => {}, Bcc => {});
-if ($Object->Scrips) {
-    for my $scrip (grep $_->ActionObj->Action->isa('RT::Action::SendEmail'), @{$Object->Scrips->Prepared}) {
+my @scrips = map {@{$_->Scrips->Prepared}} @dryrun;
+if (@scrips) {
+    for my $scrip (grep $_->ActionObj->Action->isa('RT::Action::SendEmail'), @scrips) {
         my $action = $scrip->ActionObj->Action;
         for my $type (qw(To Cc Bcc)) {
             for my $addr ($action->$type()) {
@@ -80,8 +92,9 @@ if ($Object->Scrips) {
         }
     }
 }
-if ($Object->Rules) {
-    for my $rule (grep {$_->{hints} and $_->{hints}{class} eq "SendEmail"} @{$Object->Rules}) {
+my @rules = map {@{$_->Rules}} @dryrun;
+if (@rules) {
+    for my $rule (grep {$_->{hints} and $_->{hints}{class} eq "SendEmail"} @rules) {
         for my $type (qw(To Cc Bcc)) {
             $headers{$type}{$_} ||= @{[Email::Address->parse($_)]}[0] # Hate list context
                 for @{$rule->{hints}{recipients}{$type}};
@@ -127,4 +140,4 @@ my %squelched = ProcessTransactionSquelching( \%ARGS );
 % unless ($TicketObj->CurrentUserHasRight('ShowOutgoingEmail')) {
     <input type="hidden" name="TxnRecipients" value="<% join ",",sort keys %recips %>" />
 % }
-% $m->abort();
\ No newline at end of file
+% $m->abort();
diff --git a/html/Ticket/ModifyPeople.html b/html/Ticket/ModifyPeople.html
new file mode 100644
index 0000000..bda19d4
--- /dev/null
+++ b/html/Ticket/ModifyPeople.html
@@ -0,0 +1,149 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Elements/Header, Title => loc('Modify people related to ticket #[_1]', $Ticket->id) &>
+<& /Elements/Tabs &>
+
+% $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Ticket => $Ticket);
+<& /Elements/ListActions, actions => \@results &>
+
+<form method="post" action="ModifyPeople.html" name="TicketPeople">
+<input type="hidden" class="hidden" name="id" value="<%$Ticket->Id%>" />
+% $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS );
+<&| /Widgets/TitleBox, title => loc('Modify people related to ticket #[_1]', $Ticket->Id),   width => "100%", color=> "#333399", class=>'ticket-info-people' &>
+<& Elements/EditPeople, Ticket => $Ticket, UserField => $UserField, UserString => $UserString, UserOp => $UserOp, GroupString => $GroupString, GroupOp => $GroupOp, GroupField => $GroupField &>
+</&>
+<&| /Widgets/TitleBox, title => loc("Modify who receives mail for ticket #[_1]", $Ticket->Id),   width => "100%", color=> "#333399", class=>'ticket-info-squelch' &>
+<p>
+  The checked users may receive email related to this ticket depending on the
+  action taken.  Uncheck users to stop sending email to them about this ticket.
+</p>
+
+<ul>
+% for my $addr (sort keys %recips) {
+  <li>
+  <input type="hidden" name="autorecipient" value="<% $addr %>">
+  <input type="checkbox" name="checked_recipient" id="checked_recipient_<%$addr%>" value="<%$addr%>" <% $recips{$addr} ? "checked" : "" %>>
+  <label for="checked_recipient_<%$addr%>"><& /Elements/ShowUser, Address => Email::Address->parse($addr) &></label>
+  </li>
+% }
+</ul>
+</&>
+<& /Elements/Submit, Name => 'SubmitTicket', Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to"), color => "#333399" &>
+</form>
+
+% $m->callback(CallbackName => 'AfterForm', ARGSRef => \%ARGS, Ticket => $Ticket);
+
+<%INIT>
+
+my @results;
+
+my $Ticket = LoadTicket($id);
+$m->callback( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+# Update the squelch list
+my %squelched = map {$_->Content => 1} $Ticket->SquelchMailTo;
+my %checked = map {$_ => 1} grep {defined}
+    (ref $ARGS{'checked_recipient'} eq "ARRAY" ? @{$ARGS{'checked_recipient'}}
+         : defined $ARGS{'checked_recipient'} ? ($ARGS{'checked_recipient'}) : ());
+my @all = grep {defined}
+    (ref $ARGS{'autorecipient'} eq "ARRAY" ? @{$ARGS{'autorecipient'}}
+         : defined $ARGS{'autorecipient'} ? ($ARGS{'autorecipient'}) : ());
+$Ticket->UnsquelchMailTo($_)
+    for grep {$squelched{$_}} keys %checked;
+$Ticket->SquelchMailTo($_)
+    for grep {!$squelched{$_} and !$checked{$_}} @all;
+
+# if we're trying to search for watchers and nothing else
+unless ($OnlySearchForPeople or $OnlySearchForGroup) {
+    push @results, ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS);
+    push @results, ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS);
+    push @results, ProcessObjectCustomFieldUpdates( TicketObj => $Ticket, ARGSRef => \%ARGS );
+
+    $Ticket->ApplyTransactionBatch;
+}
+
+# Use the ticket's scrips to figure out the new list of recipients.
+my @txns = $Ticket->DryRun(
+    sub {
+        my $MIME = MIME::Entity->build( Type => "text/plain", Data => "" );
+        $Ticket->Comment(MIMEObj => $MIME);
+        $Ticket->Correspond(MIMEObj => $MIME);
+    }
+);
+my %recips=();
+for my $scrip (map {@{$_->Scrips->Prepared}} @txns) {
+    next unless $scrip->ActionObj->Action->isa('RT::Action::SendEmail');
+    for my $type (qw(To Cc Bcc)) {
+        $recips{$_->address} = 1 for $scrip->ActionObj->Action->$type();
+    }
+}
+for my $rule (map {@{$_->Rules}} @txns) {
+    next unless $rule->{hints} && $rule->{hints}{class} eq "SendEmail";
+    for my $type (qw(To Cc Bcc)) {
+        $recips{$_} = 1 for @{$rule->{hints}{recips}{$type}};
+    }
+}
+
+# Use tkt squelch list to get recipients who will NOT get mail:
+$recips{$_->Content} = 0 for $Ticket->SquelchMailTo;
+</%INIT>
+
+
+
+<%ARGS>
+$OnlySearchForPeople => undef
+$OnlySearchForGroup => undef
+$UserField => undef
+$UserOp => undef
+$UserString => undef
+$GroupField => undef
+$GroupOp => undef
+$GroupString => undef
+$id => undef
+</%ARGS>
+
diff --git a/lib/RT/Extension/AjaxPreviewScrips.pm b/lib/RT/Extension/AjaxPreviewScrips.pm
index 8a40d70..3e5e680 100644
--- a/lib/RT/Extension/AjaxPreviewScrips.pm
+++ b/lib/RT/Extension/AjaxPreviewScrips.pm
@@ -41,6 +41,316 @@ May need root permissions
 
 =back
 
+=cut
+
+no warnings 'redefine';
+
+package HTML::Mason::Commands;
+use vars qw/%session/;
+sub CreateTicket {
+    my %ARGS = (@_);
+
+    my (@Actions);
+
+    my $Ticket = delete $ARGS{TicketObj} || RT::Ticket->new( $session{'CurrentUser'} );
+
+    my $Queue = RT::Queue->new( $session{'CurrentUser'} );
+    unless ( $Queue->Load( $ARGS{'Queue'} ) ) {
+        Abort('Queue not found');
+    }
+
+    unless ( $Queue->CurrentUserHasRight('CreateTicket') ) {
+        Abort('You have no permission to create tickets in that queue.');
+    }
+
+    my $due;
+    if ( defined $ARGS{'Due'} and $ARGS{'Due'} =~ /\S/ ) {
+        $due = RT::Date->new( $session{'CurrentUser'} );
+        $due->Set( Format => 'unknown', Value => $ARGS{'Due'} );
+    }
+    my $starts;
+    if ( defined $ARGS{'Starts'} and $ARGS{'Starts'} =~ /\S/ ) {
+        $starts = RT::Date->new( $session{'CurrentUser'} );
+        $starts->Set( Format => 'unknown', Value => $ARGS{'Starts'} );
+    }
+
+    my $sigless = RT::Interface::Web::StripContent(
+        Content        => $ARGS{Content},
+        ContentType    => $ARGS{ContentType},
+        StripSignature => 1,
+        CurrentUser    => $session{'CurrentUser'},
+    );
+
+    my $MIMEObj = MakeMIMEEntity(
+        Subject => $ARGS{'Subject'},
+        From    => $ARGS{'From'},
+        Cc      => $ARGS{'Cc'},
+        Body    => $sigless,
+        Type    => $ARGS{'ContentType'},
+        Interface => RT::Interface::Web::MobileClient() ? 'Mobile' : 'Web',
+    );
+
+    my @attachments;
+    if ( my $tmp = $session{'Attachments'}{ $ARGS{'Token'} || '' } ) {
+        push @attachments, grep $_, map $tmp->{$_}, sort keys %$tmp;
+
+        delete $session{'Attachments'}{ $ARGS{'Token'} || '' }
+            unless $ARGS{'KeepAttachments'};
+        $session{'Attachments'} = $session{'Attachments'}
+            if @attachments;
+    }
+    if ( $ARGS{'Attachments'} ) {
+        push @attachments, grep $_, map $ARGS{Attachments}->{$_}, sort keys %{ $ARGS{'Attachments'} };
+    }
+    if ( @attachments ) {
+        $MIMEObj->make_multipart;
+        $MIMEObj->add_part( $_ ) foreach @attachments;
+    }
+
+    for my $argument (qw(Encrypt Sign)) {
+        if ( defined $ARGS{ $argument } ) {
+            $MIMEObj->head->replace( "X-RT-$argument" => $ARGS{$argument} ? 1 : 0 );
+        }
+    }
+
+    my %create_args = (
+        Type => $ARGS{'Type'} || 'ticket',
+        Queue => $ARGS{'Queue'},
+        Owner => $ARGS{'Owner'},
+
+        # note: name change
+        Requestor       => $ARGS{'Requestors'},
+        Cc              => $ARGS{'Cc'},
+        AdminCc         => $ARGS{'AdminCc'},
+        InitialPriority => $ARGS{'InitialPriority'},
+        FinalPriority   => $ARGS{'FinalPriority'},
+        TimeLeft        => $ARGS{'TimeLeft'},
+        TimeEstimated   => $ARGS{'TimeEstimated'},
+        TimeWorked      => $ARGS{'TimeWorked'},
+        Subject         => $ARGS{'Subject'},
+        Status          => $ARGS{'Status'},
+        Due             => $due ? $due->ISO : undef,
+        Starts          => $starts ? $starts->ISO : undef,
+        MIMEObj         => $MIMEObj,
+        TransSquelchMailTo => $ARGS{'TransSquelchMailTo'},
+    );
+
+    my @txn_squelch;
+    foreach my $type (qw(Requestor Cc AdminCc)) {
+        push @txn_squelch, map $_->address, Email::Address->parse( $create_args{$type} )
+            if grep $_ eq $type || $_ eq ( $type . 's' ), @{ $ARGS{'SkipNotification'} || [] };
+    }
+    push @{$create_args{TransSquelchMailTo}}, @txn_squelch;
+
+    if ( $ARGS{'AttachTickets'} ) {
+        require RT::Action::SendEmail;
+        RT::Action::SendEmail->AttachTickets( RT::Action::SendEmail->AttachTickets,
+            ref $ARGS{'AttachTickets'}
+            ? @{ $ARGS{'AttachTickets'} }
+            : ( $ARGS{'AttachTickets'} ) );
+    }
+
+    my %cfs = ProcessObjectCustomFieldUpdatesForCreate(
+        ARGSRef         => \%ARGS,
+        ContextObject   => $Queue,
+    );
+
+    my %links = ProcessLinksForCreate( ARGSRef => \%ARGS );
+
+    my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args, %links, %cfs);
+
+    unless ($id) {
+        Abort($ErrMsg);
+    }
+
+    push( @Actions, split( "\n", $ErrMsg ) );
+    unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
+        Abort( "No permission to view newly created ticket #" . $Ticket->id . "." );
+    }
+    return ( $Ticket, @Actions );
+
+}
+
+my $_ProcessUpdateMessageRecipients = \&_ProcessUpdateMessageRecipients;
+*_ProcessUpdateMessageRecipients = sub {
+    $_ProcessUpdateMessageRecipients->(@_);
+
+    my %args = (
+        TicketObj         => undef,
+        MessageArgs       => undef,
+        @_,
+    );
+    $args{TicketObj}{TransSquelchMailTo} ||= $args{message_args}{'SquelchMailTo'};
+};
+
+
+package RT::Record;
+sub _NewTransaction {
+    my $self = shift;
+    my %args = (
+        TimeTaken => undef,
+        Type      => undef,
+        OldValue  => undef,
+        NewValue  => undef,
+        OldReference  => undef,
+        NewReference  => undef,
+        ReferenceType => undef,
+        Data      => undef,
+        Field     => undef,
+        MIMEObj   => undef,
+        ActivateScrips => 1,
+        SquelchMailTo => undef,
+        @_
+    );
+
+    my $in_txn = RT->DatabaseHandle->TransactionDepth;
+    RT->DatabaseHandle->BeginTransaction unless $in_txn;
+
+    $self->LockForUpdate;
+
+    my $old_ref = $args{'OldReference'};
+    my $new_ref = $args{'NewReference'};
+    my $ref_type = $args{'ReferenceType'};
+    if ($old_ref or $new_ref) {
+        $ref_type ||= ref($old_ref) || ref($new_ref);
+        if (!$ref_type) {
+            $RT::Logger->error("Reference type not specified for transaction");
+            return;
+        }
+        $old_ref = $old_ref->Id if ref($old_ref);
+        $new_ref = $new_ref->Id if ref($new_ref);
+    }
+
+    require RT::Transaction;
+    my $trans = RT::Transaction->new( $self->CurrentUser );
+    my ( $transaction, $msg ) = $trans->Create(
+        ObjectId  => $self->Id,
+        ObjectType => ref($self),
+        TimeTaken => $args{'TimeTaken'},
+        Type      => $args{'Type'},
+        Data      => $args{'Data'},
+        Field     => $args{'Field'},
+        NewValue  => $args{'NewValue'},
+        OldValue  => $args{'OldValue'},
+        NewReference  => $new_ref,
+        OldReference  => $old_ref,
+        ReferenceType => $ref_type,
+        MIMEObj   => $args{'MIMEObj'},
+        ActivateScrips => $args{'ActivateScrips'},
+        DryRun => $self->{DryRun},
+        SquelchMailTo => $args{'SquelchMailTo'} || $self->{TransSquelchMailTo},
+    );
+
+    # Rationalize the object since we may have done things to it during the caching.
+    $self->Load($self->Id);
+
+    $RT::Logger->warning($msg) unless $transaction;
+
+    $self->_SetLastUpdated;
+
+    if ( defined $args{'TimeTaken'} and $self->can('_UpdateTimeTaken')) {
+        $self->_UpdateTimeTaken( $args{'TimeTaken'}, Transaction => $trans );
+    }
+    if ( RT->Config->Get('UseTransactionBatch') and $transaction ) {
+        push @{$self->{_TransactionBatch}}, $trans;
+    }
+
+    RT->DatabaseHandle->Commit unless $in_txn;
+
+    return ( $transaction, $msg, $trans );
+}
+
+package RT::Ticket;
+sub DryRun {
+    my $self = shift;
+
+    my ($subref) = @_;
+
+    my @transactions;
+
+    $RT::Handle->BeginTransaction();
+    {
+        # Getting nested "commit"s inside this rollback is fine
+        local %DBIx::SearchBuilder::Handle::TRANSROLLBACK;
+        local $self->{DryRun} = \@transactions;
+        eval { $subref->() };
+        warn "Error is $@" if $@;
+        $self->ApplyTransactionBatch;
+    }
+
+    @transactions = grep {$_} @transactions;
+
+    $RT::Handle->Rollback();
+
+    return wantarray ? @transactions : $transactions[0];
+}
+
+sub _ApplyTransactionBatch {
+    my $self = shift;
+
+    return if $self->RanTransactionBatch;
+    $self->RanTransactionBatch(1);
+
+    my $still_exists = RT::Ticket->new( RT->SystemUser );
+    $still_exists->Load( $self->Id );
+    if (not $still_exists->Id) {
+        # The ticket has been removed from the database, but we still
+        # have pending TransactionBatch txns for it.  Unfortunately,
+        # because it isn't in the DB anymore, attempting to run scrips
+        # on it may produce unpredictable results; simply drop the
+        # batched transactions.
+        $RT::Logger->warning("TransactionBatch was fired on a ticket that no longer exists; unable to run scrips!  Call ->ApplyTransactionBatch before shredding the ticket, for consistent results.");
+        return;
+    }
+
+    my $batch = $self->TransactionBatch;
+
+    my %seen;
+    my $types = join ',', grep !$seen{$_}++, grep defined, map $_->__Value('Type'), grep defined, @{$batch};
+
+    require RT::Scrips;
+    my $scrips = RT::Scrips->new(RT->SystemUser);
+    $scrips->Prepare(
+        Stage          => 'TransactionBatch',
+        TicketObj      => $self,
+        TransactionObj => $batch->[0],
+        Type           => $types,
+    );
+
+    # Entry point of the rule system
+    my $rules = RT::Ruleset->FindAllRules(
+        Stage          => 'TransactionBatch',
+        TicketObj      => $self,
+        TransactionObj => $batch->[0],
+        Type           => $types,
+    );
+
+    if ($self->{DryRun}) {
+        my $fake_txn = RT::Transaction->new( $self->CurrentUser );
+        $fake_txn->{scrips} = $scrips;
+        $fake_txn->{rules} = $rules;
+        push @{$self->{DryRun}}, $fake_txn;
+    } else {
+        $scrips->Commit;
+        RT::Ruleset->CommitRules($rules);
+    }
+}
+
+
+package RT::Transaction;
+
+my $Create = \&Create;
+*Create = sub {
+    my $self = shift;
+    my %args = (@_);
+    $args{CommitScrips} = 0 if $args{DryRun};
+    my @retval = $Create->($self, %args);
+    push @{$args{DryRun}}, $self if $args{DryRun} and $retval[0];
+
+    return wantarray ? @retval : $retval[0];
+};
+
+
 =head1 AUTHOR
 
 sunnavy <sunnavy at bestpractical.com>

-----------------------------------------------------------------------


More information about the Bps-public-commit mailing list