[Rt-commit] r11245 - in rt/branches/3.8-TESTING: lib/RT/Action
ruz at bestpractical.com
ruz at bestpractical.com
Thu Mar 27 17:44:03 EDT 2008
Author: ruz
Date: Thu Mar 27 17:44:02 2008
New Revision: 11245
Added:
rt/branches/3.8-TESTING/lib/RT/Action/NotifyGroup.pm
rt/branches/3.8-TESTING/lib/RT/Action/NotifyGroupAsComment.pm
rt/branches/3.8-TESTING/sbin/rt-notify-group-admin.in (contents, props changed)
Log:
* add Action::NotifyGroup
Added: rt/branches/3.8-TESTING/lib/RT/Action/NotifyGroup.pm
==============================================================================
--- (empty file)
+++ rt/branches/3.8-TESTING/lib/RT/Action/NotifyGroup.pm Thu Mar 27 17:44:02 2008
@@ -0,0 +1,147 @@
+=head1 NAME
+
+RT::Action::NotifyGroup - RT Action that sends notifications to groups and/or users
+
+=head1 DESCRIPTION
+
+RT action module that allow you to notify particular groups and/or users.
+Distribution is shipped with C<rt-notify-group-admin> script that
+is command line tool for managing NotifyGroup scrip actions. For more
+more info see its documentation.
+
+=cut
+
+package RT::Action::NotifyGroup;
+
+use strict;
+use warnings;
+use base qw(RT::Action::Notify);
+
+require RT::User;
+require RT::Group;
+
+=head1 METHODS
+
+=head2 SetRecipients
+
+Sets the recipients of this message to Groups and/or Users.
+
+=cut
+
+sub SetRecipients {
+ my $self = shift;
+
+ my $arg = $self->Argument;
+
+ my $old_arg = eval { Storable::thaw( $arg ) };
+ unless( $@ ) {
+ $arg = $self->__ConvertOldArg( $old_arg );
+ }
+
+ foreach( $self->__SplitArg( $arg ) ) {
+ $self->_HandleArgument( $_ );
+ }
+
+ my $creator = $self->TransactionObj->CreatorObj->EmailAddress();
+ unless( $RT::NotifyActor ) {
+ @{ $self->{'To'} } = grep ( !/^\Q$creator\E$/, @{ $self->{'To'} } );
+ }
+
+ $self->{'seen_ueas'} = {};
+
+ return 1;
+}
+
+sub _HandleArgument {
+ my $self = shift;
+ my $instance = shift;
+
+ my $obj = RT::Principal->new( $RT::SystemUser );
+ $obj->Load( $instance );
+ unless( $obj->id ) {
+ $RT::Logger->error( "Couldn't load principal #$instance" );
+ return;
+ }
+ if( $obj->Disabled ) {
+ $RT::Logger->info( "Principal #$instance is disabled => skip" );
+ return;
+ }
+ if( !$obj->PrincipalType ) {
+ $RT::Logger->crit( "Principal #$instance has empty type" );
+ } elsif( lc $obj->PrincipalType eq 'user' ) {
+ $self->__HandleUserArgument( $obj->Object );
+ } elsif( lc $obj->PrincipalType eq 'group' ) {
+ $self->__HandleGroupArgument( $obj->Object );
+ } else {
+ $RT::Logger->info( "Principal #$instance has unsupported type" );
+ }
+ return;
+}
+
+sub __HandleUserArgument {
+ my $self = shift;
+ my $obj = shift;
+
+ my $uea = $obj->EmailAddress;
+ unless( $uea ) {
+ $RT::Logger->warning( "User #". $obj->id ." has no email address" );
+ return;
+ }
+ $self->__PushUserAddress( $uea );
+}
+
+sub __HandleGroupArgument {
+ my $self = shift;
+ my $obj = shift;
+
+ my $members = $obj->UserMembersObj;
+ while( my $m = $members->Next ) {
+ $self->__HandleUserArgument( $m );
+ }
+}
+
+sub __SplitArg {
+ return split /[^0-9]+/, $_[1];
+}
+
+sub __ConvertOldArg {
+ my $self = shift;
+ my $arg = shift;
+ my @res;
+ foreach my $r ( @{ $arg } ) {
+ my $obj;
+ next unless $r->{'Type'};
+ if( lc $r->{'Type'} eq 'user' ) {
+ $obj = RT::User->new( $RT::SystemUser );
+ } elsif ( lc $r->{'Type'} eq 'user' ) {
+ $obj = RT::Group->new( $RT::SystemUser );
+ } else {
+ next;
+ }
+ $obj->Load( $r->{'Instance'} );
+ my $id = $obj->id;
+ next unless( $id );
+
+ push @res, $id;
+ }
+
+ return join ';', @res;
+}
+
+sub __PushUserAddress {
+ my $self = shift;
+ my $uea = shift;
+ push @{ $self->{'To'} }, $uea unless $self->{'seen_ueas'}{ $uea }++;
+ return;
+}
+
+
+=head1 AUTHOR
+
+Ruslan U. Zakirov E<lt>ruz at bestpractical.comE<gt>
+
+L<RT::Action::NotifyGroupAsComment>, F<rt-notify-group-admin>
+
+=cut
+
+1;
Added: rt/branches/3.8-TESTING/lib/RT/Action/NotifyGroupAsComment.pm
==============================================================================
--- (empty file)
+++ rt/branches/3.8-TESTING/lib/RT/Action/NotifyGroupAsComment.pm Thu Mar 27 17:44:02 2008
@@ -0,0 +1,33 @@
+=head1 NAME
+
+RT::Action::NotifyGroupAsComment - RT Action that sends notifications to groups and/or users as comment
+
+=head1 DESCRIPTION
+
+This is subclass of L<RT::Action::NotifyGroup> that send comments instead of replies.
+See C<rt-notify-group-admin> and L<RT::Action::NotifyGroup> docs for more info.
+
+=cut
+
+package RT::Action::NotifyGroupAsComment;
+
+use strict;
+use warnings;
+
+use RT::Action::NotifyGroup;
+
+use base qw(RT::Action::NotifyGroup);
+
+sub SetReturnAddress {
+ my $self = shift;
+ $self->{'comment'} = 1;
+ return $self->SUPER::SetReturnAddress( @_, is_comment => 1 );
+}
+
+=head1 AUTHOR
+
+Ruslan U. Zakirov E<lt>ruz at bestpractical.comE<gt>
+
+=cut
+
+1;
Added: rt/branches/3.8-TESTING/sbin/rt-notify-group-admin.in
==============================================================================
--- (empty file)
+++ rt/branches/3.8-TESTING/sbin/rt-notify-group-admin.in Thu Mar 27 17:44:02 2008
@@ -0,0 +1,476 @@
+#!@PERL@
+
+=head1 NAME
+
+rt-notify-group-admin - Command line tool for administrating NotifyGroup actions
+
+=head1 SYNOPSIS
+
+ rt-notify-group-admin --list
+ rt-notify-group-admin --create 'Notify foo team' --group Foo
+ rt-notify-group-admin --create 'Notify foo team as comment' --comment --group Foo
+ rt-notify-group-admin --create 'Notify group Foo and Bar' --group Foo --group Bar
+ rt-notify-group-admin --create 'Notify user foo at bar.com' --user foo at bar.com
+ rt-notify-group-admin --create 'Notify VIPs' --user vip1 at bar.com
+ rt-notify-group-admin --add 'Notify VIPs' --user vip2 at bar.com --group vip1 --user vip3 at foo.com
+ rt-notify-group-admin --rename 'Notify VIPs' --newname 'Inform VIPs'
+ rt-notify-group-admin --switch 'Notify VIPs'
+ rt-notify-group-admin --delete 'Notify user foo at bar.com'
+
+=head1 DESCRIPTION
+
+This script list, create, modify or delete scrip actions in the RT DB. Once
+you've created an action you can use it in a scrip.
+
+For example you can create the following action using this script:
+
+ rt-notify-group-admin --create 'Notify developers' --group 'Development Team'
+
+Then you can add the followoing scrip to your Bugs queue:
+
+ Condition: On Create
+ Action: Notify developers
+ Template: Transaction
+ Stage: TransactionCreate
+
+Your development team will be notified on every new ticket in the queue.
+
+=cut
+
+use warnings;
+use strict;
+
+use lib "@LOCAL_LIB_PATH@";
+use lib "@RT_LIB_PATH@";
+
+use RT;
+RT::LoadConfig;
+RT::Init;
+
+require RT::Principal;
+require RT::User;
+require RT::Group;
+require RT::ScripActions;
+
+use Getopt::Long qw(GetOptions);
+
+our $cmd = 'usage';
+our $opts = {};
+
+sub parse_args {
+ my $tmp;
+ Getopt::Long::Configure( "pass_through" );
+ if ( GetOptions( 'list' => \$tmp ) && $tmp ) {
+ $cmd = 'list';
+ }
+ elsif ( GetOptions( 'create=s' => \$tmp ) && $tmp ) {
+ $cmd = 'create';
+ $opts->{'name'} = $tmp;
+ $opts->{'groups'} = [];
+ $opts->{'users'} = [];
+ GetOptions( 'comment' => \$opts->{'comment'} );
+ GetOptions( 'group:s@' => $opts->{'groups'} );
+ GetOptions( 'user:s@' => $opts->{'users'} );
+ unless ( @{ $opts->{'users'} } + @{ $opts->{'groups'} } ) {
+ usage();
+ exit(-1);
+ }
+ }
+ elsif ( GetOptions( 'add=s' => \$tmp ) && $tmp ) {
+ $cmd = 'add';
+ $opts->{'name'} = $tmp;
+ $opts->{'groups'} = [];
+ $opts->{'users'} = [];
+ GetOptions( 'group:s@' => $opts->{'groups'} );
+ GetOptions( 'user:s@' => $opts->{'users'} );
+ unless ( @{ $opts->{'users'} } + @{ $opts->{'groups'} } ) {
+ usage();
+ exit(-1);
+ }
+ }
+ elsif ( GetOptions( 'switch=s' => \$tmp ) && $tmp ) {
+ $cmd = 'switch';
+ $opts->{'name'} = $tmp;
+ }
+ elsif ( GetOptions( 'rename=s' => \$tmp ) && $tmp ) {
+ $cmd = 'rename';
+ $opts->{'name'} = $tmp;
+ GetOptions( 'newname=s' => \$opts->{'newname'} );
+ unless ( $opts->{'newname'} ) {
+ usage();
+ exit(-1);
+ }
+ }
+ elsif ( GetOptions( 'delete=s' => \$tmp ) && $tmp) {
+ $cmd = 'delete';
+ $opts->{'name'} = $tmp;
+ } else {
+ $cmd = 'usage';
+ }
+
+ return;
+}
+
+sub usage {
+ local $@;
+ eval "require Pod::PlainText;";
+ if ( $@ ) {
+ print "see `perldoc $0`\n";
+ } else {
+ my $parser = Pod::PlainText->new( sentence => 0, width => 78 );
+ $parser->parse_from_file( $0 );
+ }
+}
+
+parse_args();
+
+{
+ eval "main::$cmd()";
+ if ( $@ ) {
+ print STDERR $@ ."\n";
+ }
+}
+
+exit(0);
+
+=head1 USAGE
+
+rt-notify-group-admin --COMMAND ARGS
+
+=head1 COMMANDS
+
+=head2 list
+
+Lists actions and its descriptions.
+
+=cut
+
+sub list {
+ my $actions = _get_our_actions();
+ while( my $a = $actions->Next ) {
+ _list( $a );
+ }
+ return;
+}
+
+sub _list {
+ my $action = shift;
+
+ print "Name: ". $action->Name() ."\n";
+ print "Module: ". $action->ExecModule() ."\n";
+
+ my @princ = argument_to_list( $action );
+
+ print "Members: \n";
+ foreach( @princ ) {
+ my $obj = RT::Principal->new( $RT::SystemUser );
+ $obj->Load( $_ );
+ next unless $obj->id;
+
+ print "\t". $obj->PrincipalType;
+ print "\t=> ". $obj->Object->Name;
+ print "(Disabled!!!)" if $obj->Disabled;
+ print "\n";
+ }
+ print "\n";
+ return;
+}
+
+=head2 create NAME [--comment] [--group GNAME] [--user UNAME]
+
+Creates new action with NAME and adds users and/or groups to its
+recipient list. Would be notify as comment if --comment specified.
+
+=cut
+
+sub create {
+ my $actions = RT::ScripActions->new( $RT::SystemUser );
+ $actions->Limit(
+ FIELD => 'Name',
+ VALUE => $opts->{'name'},
+ );
+ if ( $actions->Count ) {
+ print STDERR "ScripAction '". $opts->{'name'} ."' allready exists\n";
+ exit(-1);
+ }
+
+ my @groups = _check_groups( @{ $opts->{'groups'} } );
+ my @users = _check_users( @{ $opts->{'users'} } );
+ unless ( @users + @groups ) {
+ print STDERR "List of groups and users is empty\n";
+ exit(-1);
+ }
+
+ my $action = __create_empty( $opts->{'name'}, $opts->{'comment'} );
+
+ __add( $action, $_ ) foreach( @users );
+ __add( $action, $_ ) foreach( @groups );
+
+ return;
+}
+
+sub __create_empty {
+ my $name = shift;
+ my $as_comment = shift || 0;
+ require RT::ScripAction;
+ my $action = RT::ScripAction->new( $RT::SystemUser );
+ $action->Create(
+ Name => $name,
+ Description => "Created with rt-notify-group-admin script",
+ ExecModule => $as_comment? 'NotifyGroupAsComment': 'NotifyGroup',
+ Argument => '',
+ );
+
+ return $action;
+}
+
+sub _check_groups
+{
+ return grep { $_ ? 1: do { print STDERR "Group '$_' skipped, doesn't exist\n"; 0; } }
+ map { __check_group($_) } @_;
+}
+
+sub __check_group
+{
+ my $instance = shift;
+ require RT::Group;
+ my $obj = RT::Group->new( $RT::SystemUser );
+ $obj->LoadUserDefinedGroup( $instance );
+ return $obj->id ? $obj : undef;
+}
+
+sub _check_users
+{
+ return grep { $_ ? 1: do { print STDERR "User '$_' skipped, doesn't exist\n"; 0; } }
+ map { __check_user($_) } @_;
+}
+
+sub __check_user
+{
+ my $instance = shift;
+ require RT::User;
+ my $obj = RT::User->new( $RT::SystemUser );
+ $obj->Load( $instance );
+ return $obj->id ? $obj : undef;
+}
+
+=head2 add NAME [--group GNAME] [--user UNAME]
+
+Adds groups and/or users to recipients of the action NAME.
+
+=cut
+
+sub add {
+ my $action = _get_action_by_name( $opts->{'name'} );
+ unless ( $action ) {
+ print STDERR "ScripAction '". $opts->{'name'} ."' doesn't exist\n";
+ exit(-1);
+ }
+
+ my @groups = _check_groups( @{ $opts->{'groups'} } );
+ my @users = _check_users( @{ $opts->{'users'} } );
+
+ unless ( @users + @groups ) {
+ print STDERR "List of groups and users is empty\n";
+ exit(-1);
+ }
+
+ __add( $action, $_ ) foreach @users;
+ __add( $action, $_ ) foreach @groups;
+
+ return;
+}
+
+sub __add
+{
+ my $action = shift;
+ my $obj = shift;
+
+ my @cur = argument_to_list( $action );
+
+ my $id = $obj->id;
+ return if grep $_ == $id, @cur;
+
+ push @cur, $id;
+
+ return $action->__Set( Field => 'Argument', Value => join(';', @cur) );
+}
+
+=head2 delete NAME
+
+Deletes action NAME if scrips doesn't use it.
+
+=cut
+
+sub delete {
+ my $action = _get_action_by_name( $opts->{'name'} );
+ unless ( $action ) {
+ print STDERR "ScripAction '". $opts->{'name'} ."' doesn't exist\n";
+ exit(-1);
+ }
+
+ require RT::Scrips;
+ my $scrips = RT::Scrips->new( $RT::SystemUser );
+ $scrips->Limit( FIELD => 'ScripAction', VALUE => $action->id );
+ if ( $scrips->Count ) {
+ my @sid;
+ while( my $s = $scrips->Next ) {
+ push @sid, $s->id;
+ }
+ print STDERR "ScripAction '". $opts->{'name'} ."'"
+ . " is in use by Scrip(s) ". join( ", ", map "#$_", @sid )
+ . "\n";
+ exit(-1);
+ }
+
+ return __delete( $action );
+}
+
+sub __delete {
+ require DBIx::SearchBuilder::Record;
+ return DBIx::SearchBuilder::Record::Delete( shift );
+}
+
+sub _get_action_by_name {
+ my $name = shift;
+ my $actions = _get_our_actions();
+ $actions->Limit(
+ FIELD => 'Name',
+ VALUE => $name
+ );
+
+ if ( $actions->Count > 1 ) {
+ print STDERR "More then one ScripAction with name '$name'\n";
+ }
+
+ return $actions->First;
+}
+
+=head2 switch NAME
+
+Switch action NAME from notify as correspondence to comment and back.
+
+=cut
+
+sub switch {
+ my $action = _get_action_by_name( $opts->{'name'} );
+ unless ( $action ) {
+ print STDERR "ScripAction '". $opts->{'name'} ."' doesn't exist\n";
+ exit(-1);
+ }
+
+ my %h = (
+ NotifyGroup => 'NotifyGroupAsComment',
+ NotifyGroupAsComment => 'NotifyGroup'
+ );
+
+ return $action->__Set(
+ Field => 'ExecModule',
+ Value => $h{ $action->ExecModule }
+ );
+}
+
+=head2 rename NAME --newname NEWNAME
+
+Renames action NAME to NEWNAME.
+
+=cut
+
+sub rename {
+ my $action = _get_action_by_name( $opts->{'name'} );
+ unless ( $action ) {
+ print STDERR "ScripAction '". $opts->{'name'} ."' doesn't exist\n";
+ exit(-1);
+ }
+
+ my $actions = RT::ScripActions->new( $RT::SystemUser );
+ $actions->Limit( FIELD => 'Name', VALUE => $opts->{'newname'} );
+ if ( $actions->Count ) {
+ print STDERR "ScripAction '". $opts->{'newname'} ."' allready exists\n";
+ exit(-1);
+ }
+
+ return $action->__Set(
+ Field => 'Name',
+ Value => $opts->{'newname'},
+ );
+}
+
+=head2 NOTES
+
+If command has option --group or --user then you can use it more then once,
+if other is not specified.
+
+=cut
+
+###############
+#### Utils ####
+###############
+
+sub argument_to_list {
+ my $action = shift;
+ my $arg = $action->Argument;
+
+ # old variant via Storable
+ {
+ local $@;
+ require Storable;
+ my $old = eval { Storable::thaw( $arg ) };
+ unless ( $@ ) {
+ $arg = __convert_old($old);
+ }
+ }
+
+ return _split_arg( $arg );
+}
+
+sub _split_arg { return split /[^0-9]+/, $_[0] }
+
+sub __convert_old {
+ my $arg = shift;
+ my @res;
+ foreach my $r ( @{ $arg } ) {
+ my $obj;
+ next unless $r->{'Type'};
+ if ( lc $r->{'Type'} eq 'user' ) {
+ $obj = RT::User->new( $RT::SystemUser );
+ } elsif ( lc $r->{'Type'} eq 'user' ) {
+ $obj = RT::Group->new( $RT::SystemUser );
+ } else {
+ next;
+ }
+ $obj->Load( $r->{'Instance'} );
+ my $id = $obj->id;
+ next unless $id;
+
+ push @res, $id;
+ }
+
+ return join ';', @res;
+}
+
+sub _get_our_actions {
+ my $actions = RT::ScripActions->new( $RT::SystemUser );
+ $actions->Limit(
+ FIELD => 'ExecModule',
+ VALUE => 'NotifyGroup',
+ ENTRYAGGREGATOR => 'OR',
+ );
+ $actions->Limit(
+ FIELD => 'ExecModule',
+ VALUE => 'NotifyGroupAsComment',
+ ENTRYAGGREGATOR => 'OR',
+ );
+
+ return $actions;
+}
+
+=head1 AUTHOR
+
+Ruslan U. Zakirov E<lt>ruz at bestpractical.comE<gt>
+
+=head1 SEE ALSO
+
+L<RT::Action::NotifyGroup>, L<RT::Action::NotifyGroupAsComment>
+
+=cut
More information about the Rt-commit
mailing list