package RTx::Shredder::Plugin::Users; use strict; use warnings FATAL => 'all'; use base qw(RTx::Shredder::Plugin::Base); =head1 NAME RTx::Shredder::Plugin::Users - search plugin for wiping users. =cut sub Type { return 'search' } =head1 ARGUMENTS =head2 status - string Status argument allow you to limit result set to C, C or C users. B<< Default value is C. >> =head2 name - mask User name mask. =head2 email - mask Email address mask. =head2 replace_relations - user identifier When you delete user there is could be minor links to him in RT DB. This option allow you to replace this links with link to other user. This links are Creator and LastUpdatedBy, but NOT any watcher roles, this mean that if user is watcher(Requestor, Owner, Cc or AdminCc) of the ticket or queue then link would be deleted. This argument could be user id or name. =head2 no_tickets - boolean If true then plugin looks for users who are not watchers (Owners, Requestors, Ccs or AdminCcs) of any ticket. B that found users still may have relations with other objects and you most probably want to use C option. =cut sub SupportArgs { return $_[0]->SUPER::SupportArgs, qw(status name email replace_relations no_tickets); } sub TestArgs { my $self = shift; my %args = @_; if( $args{'status'} ) { unless( $args{'status'} =~ /^(disabled|enabled|any)$/i ) { return (0, "Status '$args{'status'}' is unsupported."); } } else { $args{'status'} = 'disabled'; } if( $args{'email'} ) { $args{'email'} = $self->ConvertMaskToSQL( $args{'email'} ); } if( $args{'name'} ) { $args{'name'} = $self->ConvertMaskToSQL( $args{'name'} ); } if( $args{'replace_relations'} ) { my $uid = $args{'replace_relations'}; # XXX: it's possible that SystemUser is not available my $user = RT::User->new( $RT::SystemUser ); $user->Load( $uid ); unless( $user->id ) { return (0, "Couldn't load user '$uid'" ); } $args{'replace_relations'} = $user->id; } return $self->SUPER::TestArgs( %args ); } sub Run { my $self = shift; my %args = ( Shredder => undef, @_ ); my $objs = RT::Users->new( $RT::SystemUser ); if( my $s = $self->{'opt'}{'status'} ) { if( $s eq 'any' ) { $objs->{'find_disabled_rows'} = 1; } elsif( $s eq 'disabled' ) { $objs->{'find_disabled_rows'} = 1; $objs->Limit( ALIAS => $objs->PrincipalsAlias, FIELD => 'Disabled', OPERATOR => '!=', VALUE => '0', ); } else { $objs->LimitToEnabled; } } if( $self->{'opt'}{'email'} ) { $objs->Limit( FIELD => 'EmailAddress', OPERATOR => 'MATCHES', VALUE => $self->{'opt'}{'email'}, ); } if( $self->{'opt'}{'name'} ) { $objs->Limit( FIELD => 'Name', OPERATOR => 'MATCHES', VALUE => $self->{'opt'}{'name'}, ); } if( $self->{'opt'}{'no_tickets'} ) { return $self->FilterWithoutTickets( Shredder => $args{'Shredder'}, Objects => $objs, ); } else { if( $self->{'opt'}{'limit'} ) { $objs->RowsPerPage( $self->{'opt'}{'limit'} ); } } return (1, $objs); } sub SetResolvers { my $self = shift; my %args = ( Shredder => undef, @_ ); if( $self->{'opt'}{'replace_relations'} ) { my $uid = $self->{'opt'}{'replace_relations'}; my $resolver = sub { my %args = (@_); my $t = $args{'TargetObject'}; foreach my $method ( qw(Creator LastUpdatedBy) ) { next unless $t->_Accessible( $method => 'read' ); $t->__Set( Field => $method, Value => $uid ); } }; $args{'Shredder'}->PutResolver( BaseClass => 'RT::User', Code => $resolver ); } return (1); } use constant PAGE_SIZE => 100; sub FetchNext { my ($self, $objs, $init) = @_; if ( $init ) { $objs->RowsPerPage( PAGE_SIZE ); $objs->FirstPage; return; } for (1..3) { my $obj = $objs->Next; return $obj if $obj; $objs->NextPage; } } sub FilterWithoutTickets { my $self = shift; my %args = ( Shredder => undef, Objects => undef, @_, ); my $users = $args{Objects}; $self->FetchNext( $users, 'init' ); my @res; while ( my $user = $self->FetchNext( $users ) ) { push @res, $user if $self->_WithoutTickets( $user ); return (1, \@res) if $self->{'opt'}{'limit'} && @res >= $self->{'opt'}{'limit'}; } return (1, \@res); } sub _WithoutTickets { my ($self, $user) = @_; my $tickets = RT::Tickets->new( $RT::SystemUser ); $tickets->FromSQL( 'Watcher.id = '. $user->id ); # HACK: Count - is count all that match condtion, # but we really want to know if at least one record record exist, # so we fetch first only $tickets->RowsPerPage(1); return !$tickets->First; } 1;