summaryrefslogtreecommitdiff
path: root/rt/lib/RT/Shredder
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib/RT/Shredder')
-rw-r--r--rt/lib/RT/Shredder/ACE.pm101
-rw-r--r--rt/lib/RT/Shredder/Attachment.pm136
-rw-r--r--rt/lib/RT/Shredder/CachedGroupMember.pm151
-rw-r--r--rt/lib/RT/Shredder/Constants.pm141
-rw-r--r--rt/lib/RT/Shredder/CustomField.pm126
-rw-r--r--rt/lib/RT/Shredder/CustomFieldValue.pm94
-rw-r--r--rt/lib/RT/Shredder/Dependencies.pm149
-rw-r--r--rt/lib/RT/Shredder/Dependency.pm112
-rw-r--r--rt/lib/RT/Shredder/Exceptions.pm113
-rw-r--r--rt/lib/RT/Shredder/Group.pm185
-rw-r--r--rt/lib/RT/Shredder/GroupMember.pm183
-rw-r--r--rt/lib/RT/Shredder/Link.pm140
-rw-r--r--rt/lib/RT/Shredder/ObjectCustomFieldValue.pm116
-rw-r--r--rt/lib/RT/Shredder/POD.pm131
-rw-r--r--rt/lib/RT/Shredder/Plugin.pm249
-rw-r--r--rt/lib/RT/Shredder/Plugin/Attachments.pm141
-rw-r--r--rt/lib/RT/Shredder/Plugin/Base.pm188
-rw-r--r--rt/lib/RT/Shredder/Plugin/Base/Dump.pm70
-rw-r--r--rt/lib/RT/Shredder/Plugin/Base/Search.pm142
-rw-r--r--rt/lib/RT/Shredder/Plugin/Objects.pm107
-rw-r--r--rt/lib/RT/Shredder/Plugin/SQLDump.pm96
-rw-r--r--rt/lib/RT/Shredder/Plugin/Summary.pm188
-rw-r--r--rt/lib/RT/Shredder/Plugin/Tickets.pm153
-rw-r--r--rt/lib/RT/Shredder/Plugin/Users.pm260
-rw-r--r--rt/lib/RT/Shredder/Principal.pm127
-rw-r--r--rt/lib/RT/Shredder/Queue.pm106
-rw-r--r--rt/lib/RT/Shredder/Record.pm273
-rw-r--r--rt/lib/RT/Shredder/Scrip.pm130
-rw-r--r--rt/lib/RT/Shredder/ScripAction.pm100
-rw-r--r--rt/lib/RT/Shredder/ScripCondition.pm101
-rw-r--r--rt/lib/RT/Shredder/Template.pm120
-rw-r--r--rt/lib/RT/Shredder/Ticket.pm126
-rw-r--r--rt/lib/RT/Shredder/Transaction.pm115
-rw-r--r--rt/lib/RT/Shredder/User.pm191
34 files changed, 4861 insertions, 0 deletions
diff --git a/rt/lib/RT/Shredder/ACE.pm b/rt/lib/RT/Shredder/ACE.pm
new file mode 100644
index 0000000..8512a85
--- /dev/null
+++ b/rt/lib/RT/Shredder/ACE.pm
@@ -0,0 +1,101 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::ACE ();
+package RT::ACE;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Exceptions;
+use RT::Shredder::Constants;
+use RT::Shredder::Dependencies;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+1;
+
diff --git a/rt/lib/RT/Shredder/Attachment.pm b/rt/lib/RT/Shredder/Attachment.pm
new file mode 100644
index 0000000..c72203d
--- /dev/null
+++ b/rt/lib/RT/Shredder/Attachment.pm
@@ -0,0 +1,136 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Attachment ();
+package RT::Attachment;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Exceptions;
+use RT::Shredder::Constants;
+use RT::Shredder::Dependencies;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Nested attachments
+ my $objs = RT::Attachments->new( $self->CurrentUser );
+ $objs->Limit(
+ FIELD => 'Parent',
+ OPERATOR => '=',
+ VALUE => $self->Id
+ );
+ $objs->Limit(
+ FIELD => 'id',
+ OPERATOR => '!=',
+ VALUE => $self->Id
+ );
+ push( @$list, $objs );
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Parent, nested parts
+ if( $self->Parent ) {
+ if( $self->ParentObj && $self->ParentId ) {
+ push( @$list, $self->ParentObj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no parent attachment #". $self->Parent ." object";
+ }
+ }
+
+# Transaction
+ my $obj = $self->TransactionObj;
+ if( defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related transaction #". $self->TransactionId ." object";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+1;
diff --git a/rt/lib/RT/Shredder/CachedGroupMember.pm b/rt/lib/RT/Shredder/CachedGroupMember.pm
new file mode 100644
index 0000000..1433803
--- /dev/null
+++ b/rt/lib/RT/Shredder/CachedGroupMember.pm
@@ -0,0 +1,151 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::CachedGroupMember ();
+package RT::CachedGroupMember;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependency;
+
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# deep memebership
+ my $objs = RT::CachedGroupMembers->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'Via', VALUE => $self->Id );
+ $objs->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->Id );
+ push( @$list, $objs );
+
+# principal lost group membership and lost some rights which he could delegate to
+# some body
+
+# XXX: Here is problem cause HasMemberRecursively would return true allways
+# cause we didn't delete anything yet. :(
+ # if pricipal is not member anymore(could be via other groups) then proceed
+ if( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
+ my $acl = RT::ACL->new( $self->CurrentUser );
+ $acl->LimitToPrincipal( Id => $self->GroupId );
+
+ # look into all rights that have group
+ while( my $ace = $acl->Next ) {
+ my $delegations = RT::ACL->new( $self->CurrentUser );
+ $delegations->DelegatedFrom( Id => $ace->Id );
+ $delegations->DelegatedBy( Id => $self->MemberId );
+ push( @$list, $delegations );
+ }
+ }
+
+# XXX: Do we need to delete records if user lost right 'DelegateRights'?
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+#TODO: If we plan write export tool we also should fetch parent groups
+# now we only wipeout things.
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+ my $obj = $self->MemberObj;
+ if( $obj && $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Principal #". $self->MemberId ." object.";
+ }
+
+ $obj = $self->GroupObj;
+ if( $obj && $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Principal #". $self->GroupId ." object.";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+1;
diff --git a/rt/lib/RT/Shredder/Constants.pm b/rt/lib/RT/Shredder/Constants.pm
new file mode 100644
index 0000000..a7166f3
--- /dev/null
+++ b/rt/lib/RT/Shredder/Constants.pm
@@ -0,0 +1,141 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Constants;
+
+use base qw(Exporter);
+
+=head1 NAME
+
+RT::Shredder::Constants - RT::Shredder constants that is used to mark state of RT objects.
+
+=head1 DESCRIPTION
+
+This module exports two group of bit constants.
+First group is group of flags which are used to clarify dependecies between objects, and
+second group is states of RT objects in Shredder cache.
+
+=head1 FLAGS
+
+=head2 DEPENDS_ON
+
+Targets that has such dependency flag set should be wiped out with base object.
+
+=head2 WIPE_AFTER
+
+If dependency has such flag then target object would be wiped only
+after base object. You should mark dependencies with this flag
+if two objects depends on each other, for example Group and Principal
+have such relationship, this mean Group depends on Principal record and
+that Principal record depends on the same Group record. Other examples:
+User and Principal, User and its ACL equivalence group.
+
+=head2 VARIABLE
+
+This flag is used to mark dependencies that can be resolved with changing
+value in target object. For example ticket can be created by user we can
+change this reference when we delete user.
+
+=head2 RELATES
+
+This flag is used to validate relationships integrity. Base object
+is valid only when all target objects which are marked with this flags
+exist.
+
+=cut
+
+use constant {
+ DEPENDS_ON => 0x000001,
+ WIPE_AFTER => 0x000010,
+ RELATES => 0x000100,
+ VARIABLE => 0x001000,
+};
+
+=head1 STATES
+
+=head2 ON_STACK
+
+Default state of object in Shredder cache that means that object is
+loaded and placed into cache.
+
+=head2 WIPED
+
+Objects with this state are not exist any more in DB, but perl
+object is still in memory. This state is used to be shure that
+delete query is called once.
+
+=head2 VALID
+
+Object is marked with this state only when its relationships
+are valid.
+
+=head2 INVALID
+
+=cut
+
+use constant {
+ ON_STACK => 0x00000,
+ IN_WIPING => 0x00001,
+ WIPED => 0x00010,
+ VALID => 0x00100,
+ INVALID => 0x01000,
+};
+
+our @EXPORT = qw(
+ DEPENDS_ON
+ WIPE_AFTER
+ RELATES
+ VARIABLE
+ ON_STACK
+ IN_WIPING
+ WIPED
+ VALID
+ INVALID
+ );
+
+1;
diff --git a/rt/lib/RT/Shredder/CustomField.pm b/rt/lib/RT/Shredder/CustomField.pm
new file mode 100644
index 0000000..d956373
--- /dev/null
+++ b/rt/lib/RT/Shredder/CustomField.pm
@@ -0,0 +1,126 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::CustomField ();
+package RT::CustomField;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+#TODO: Queues if we wish export tool
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Custom field values
+ push( @$list, $self->Values );
+
+# Ticket custom field values
+ my $objs = RT::ObjectCustomFieldValues->new( $self->CurrentUser );
+ $objs->LimitToCustomField( $self->Id );
+ push( @$list, $objs );
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+ my $obj = $self->Object;
+
+# Queue
+# Skip if it's global CF
+ if( $self->Queue ) {
+ if( $self->QueueObj && $self->QueueObj->Id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related queue #". $self->Queue ." object";
+ }
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
+
diff --git a/rt/lib/RT/Shredder/CustomFieldValue.pm b/rt/lib/RT/Shredder/CustomFieldValue.pm
new file mode 100644
index 0000000..56a1479
--- /dev/null
+++ b/rt/lib/RT/Shredder/CustomFieldValue.pm
@@ -0,0 +1,94 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::CustomFieldValue ();
+package RT::CustomFieldValue;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+# No dependencies that should be deleted with record
+# I should decide is TicketCustomFieldValue depends by this or not.
+# Today I think no. What would be tomorrow I don't know.
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+ my $obj = $self->CustomFieldObj;
+ if( $obj && defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related CustomField #". $self->id ." object";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Dependencies.pm b/rt/lib/RT/Shredder/Dependencies.pm
new file mode 100644
index 0000000..c4ba94c
--- /dev/null
+++ b/rt/lib/RT/Shredder/Dependencies.pm
@@ -0,0 +1,149 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Dependencies;
+
+use strict;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Constants;
+use RT::Shredder::Dependency;
+use RT::Record;
+
+
+
+=head1 METHODS
+
+=head2 new
+
+Creates new empty collection of dependecies.
+
+=cut
+
+sub new
+{
+ my $proto = shift;
+ my $self = bless( {}, ref $proto || $proto );
+ $self->{'list'} = [];
+ return $self;
+}
+
+=head2 _PushDependencies
+
+Put in objects into collection.
+Takes
+BaseObject - any supported object of RT::Record subclass;
+Flags - flags that describe relationship between target and base objects;
+TargetObjects - any of RT::SearchBuilder or RT::Record subclassed objects
+or array ref on list of this objects;
+Shredder - RT::Shredder object.
+
+SeeAlso: _PushDependecy, RT::Shredder::Dependency
+
+=cut
+
+sub _PushDependencies
+{
+ my $self = shift;
+ my %args = ( TargetObjects => undef, Shredder => undef, @_ );
+ my @objs = $args{'Shredder'}->CastObjectsToRecords( Objects => delete $args{'TargetObjects'} );
+ $self->_PushDependency( %args, TargetObject => $_ ) foreach @objs;
+ return;
+}
+
+sub _PushDependency
+{
+ my $self = shift;
+ my %args = (
+ BaseObject => undef,
+ Flags => undef,
+ TargetObject => undef,
+ Shredder => undef,
+ @_
+ );
+ my $rec = $args{'Shredder'}->PutObject( Object => $args{'TargetObject'} );
+ return if $rec->{'State'} & WIPED; # there is no object anymore
+
+ push @{ $self->{'list'} },
+ RT::Shredder::Dependency->new(
+ BaseObject => $args{'BaseObject'},
+ Flags => $args{'Flags'},
+ TargetObject => $rec->{'Object'},
+ );
+
+ if( scalar @{ $self->{'list'} } > ( $RT::DependenciesLimit || 1000 ) ) {
+ RT::Shredder::Exception::Info->throw( 'DependenciesLimit' );
+ }
+ return;
+}
+
+=head2 List
+
+
+=cut
+
+sub List
+{
+ my $self = shift;
+ my %args = (
+ WithFlags => undef,
+ WithoutFlags => undef,
+ Callback => undef,
+ @_
+ );
+
+ my $wflags = delete $args{'WithFlags'};
+ my $woflags = delete $args{'WithoutFlags'};
+
+ return
+ map $args{'Callback'}? $args{'Callback'}->($_): $_,
+ grep !defined( $wflags ) || ($_->Flags & $wflags) == $wflags,
+ grep !defined( $woflags ) || !($_->Flags & $woflags),
+ @{ $self->{'list'} };
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Dependency.pm b/rt/lib/RT/Shredder/Dependency.pm
new file mode 100644
index 0000000..bd49ac0
--- /dev/null
+++ b/rt/lib/RT/Shredder/Dependency.pm
@@ -0,0 +1,112 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Dependency;
+
+use strict;
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+
+my %FlagDescs = (
+ DEPENDS_ON, 'depends on',
+ VARIABLE, 'resolvable dependency',
+ WIPE_AFTER, 'delete after',
+ RELATES, 'relates with',
+);
+
+sub new
+{
+ my $proto = shift;
+ my $self = bless( {}, ref $proto || $proto );
+ $self->Set( @_ );
+ return $self;
+}
+
+sub Set
+{
+ my $self = shift;
+ my %args = ( Flags => DEPENDS_ON, @_ );
+ my @keys = qw(Flags BaseObject TargetObject);
+ @$self{ @keys } = @args{ @keys };
+
+ return;
+}
+
+sub AsString
+{
+ my $self = shift;
+ my $res = $self->BaseObject->_AsString;
+ $res .= " ". $self->FlagsAsString;
+ $res .= " ". $self->TargetObject->_AsString;
+ return $res;
+}
+
+sub Flags { return $_[0]->{'Flags'} }
+sub FlagsAsString
+{
+ my $self = shift;
+ my @res = ();
+ foreach ( sort keys %FlagDescs ) {
+ if( $self->Flags() & $_ ) {
+ push( @res, $FlagDescs{ $_ } );
+ }
+ }
+ push @res, 'no flags' unless( @res );
+ return "(" . join( ',', @res ) . ")";
+}
+
+
+sub BaseObject { return $_[0]->{'BaseObject'} }
+sub TargetObject { return $_[0]->{'TargetObject'} }
+sub Object { return shift()->{ ({@_})->{Type}. "Object" } }
+
+sub TargetClass { return ref $_[0]->{'TargetObject'} }
+sub BaseClass { return ref $_[0]->{'BaseObject'} }
+sub Class { return ref shift()->Object( @_ ) }
+
+1;
diff --git a/rt/lib/RT/Shredder/Exceptions.pm b/rt/lib/RT/Shredder/Exceptions.pm
new file mode 100644
index 0000000..423b7b5
--- /dev/null
+++ b/rt/lib/RT/Shredder/Exceptions.pm
@@ -0,0 +1,113 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Exception;
+
+use warnings;
+use strict;
+
+use Exception::Class;
+use base qw(Exception::Class::Base);
+
+BEGIN {
+ __PACKAGE__->NoRefs(0);
+}
+
+#sub NoRefs { return 0 }
+sub show_trace { return 1 }
+
+package RT::Shredder::Exception::Info;
+
+use base qw(RT::Shredder::Exception);
+
+my %DESCRIPTION = (
+ DependenciesLimit => <<END,
+Dependecies list have reached its limit.
+See \$RT::DependenciesLimit in RT::Shredder docs.
+END
+
+ SystemObject => <<END,
+System object was requested for deletion, shredder couldn't
+do that because system would be unusable than.
+END
+
+ CouldntLoadObject => <<END,
+Shredder couldn't load object. Most probably it's not fatal error.
+May be you've used Objects plugin and asked to delete object that
+doesn't exist in the system. If you think that your request was
+correct and it's problem of the Shredder then you can get full error
+message from RT log files and send bug report.
+END
+
+ NoResolver => <<END,
+Object has dependency that could be resolved, but resolver
+wasn't defined. You have to re-read documentation of the
+plugin you're using, for example the 'Users' plugin has
+option 'replace_relations' argument.
+END
+);
+
+sub Fields { return ((shift)->SUPER::Fields(@_), 'tag') }
+
+sub tag { return (shift)->{'tag'} }
+
+sub full_message {
+ my $self = shift;
+ my $error = $self->message;
+ if ( my $tag = $self->tag ) {
+ my $message = $DESCRIPTION{ $self->tag } || '';
+ warn "Tag '$tag' doesn't exist" unless $message;
+ $message .= "\nAdditional info:\n$error" if $error;
+ return $message;
+ }
+ return $DESCRIPTION{$error} || $error;
+}
+
+sub show_trace { return 0 }
+
+1;
diff --git a/rt/lib/RT/Shredder/Group.pm b/rt/lib/RT/Shredder/Group.pm
new file mode 100644
index 0000000..867fe02
--- /dev/null
+++ b/rt/lib/RT/Shredder/Group.pm
@@ -0,0 +1,185 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Group ();
+package RT::Group;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# User is inconsistent without own Equivalence group
+ if( $self->Domain eq 'ACLEquivalence' ) {
+ # delete user entry after ACL equiv group
+ # in other case we will get deep recursion
+ my $objs = RT::User->new($self->CurrentUser);
+ $objs->Load( $self->Instance );
+ $deps->_PushDependency(
+ BaseObject => $self,
+ Flags => DEPENDS_ON | WIPE_AFTER,
+ TargetObject => $objs,
+ Shredder => $args{'Shredder'}
+ );
+ }
+
+# Principal
+ $deps->_PushDependency(
+ BaseObject => $self,
+ Flags => DEPENDS_ON | WIPE_AFTER,
+ TargetObject => $self->PrincipalObj,
+ Shredder => $args{'Shredder'}
+ );
+
+# Group members records
+ my $objs = RT::GroupMembers->new( $self->CurrentUser );
+ $objs->LimitToMembersOfGroup( $self->PrincipalId );
+ push( @$list, $objs );
+
+# Group member records group belongs to
+ $objs = RT::GroupMembers->new( $self->CurrentUser );
+ $objs->Limit(
+ VALUE => $self->PrincipalId,
+ FIELD => 'MemberId',
+ ENTRYAGGREGATOR => 'OR',
+ QUOTEVALUE => 0
+ );
+ push( @$list, $objs );
+
+# Cached group members records
+ push( @$list, $self->DeepMembersObj );
+
+# Cached group member records group belongs to
+ $objs = RT::GroupMembers->new( $self->CurrentUser );
+ $objs->Limit(
+ VALUE => $self->PrincipalId,
+ FIELD => 'MemberId',
+ ENTRYAGGREGATOR => 'OR',
+ QUOTEVALUE => 0
+ );
+ push( @$list, $objs );
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Equivalence group id inconsistent without User
+ if( $self->Domain eq 'ACLEquivalence' ) {
+ my $obj = RT::User->new($self->CurrentUser);
+ $obj->Load( $self->Instance );
+ if( $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "ACLEguvivalence group have no related User #". $self->Instance ." object.";
+ }
+ }
+
+# Principal
+ my $obj = $self->PrincipalObj;
+ if( $obj && $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Principal #". $self->id ." object.";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+
+sub BeforeWipeout
+{
+ my $self = shift;
+ if( $self->Domain eq 'SystemInternal' ) {
+ RT::Shredder::Exception::Info->throw('SystemObject');
+ }
+ return $self->SUPER::BeforeWipeout( @_ );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/GroupMember.pm b/rt/lib/RT/Shredder/GroupMember.pm
new file mode 100644
index 0000000..d8b002e
--- /dev/null
+++ b/rt/lib/RT/Shredder/GroupMember.pm
@@ -0,0 +1,183 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::GroupMember ();
+package RT::GroupMember;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+# No dependencies that should be deleted with record
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+ my $objs = RT::CachedGroupMembers->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'MemberId', VALUE => $self->MemberId );
+ $objs->Limit( FIELD => 'ImmediateParentId', VALUE => $self->GroupId );
+ push( @$list, $objs );
+
+ # XXX: right delegations should be cleaned here
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+
+ my $group = $self->GroupObj->Object;
+ # XXX: If we delete member of the ticket owner role group then we should also
+ # fix ticket object, but only if we don't plan to delete group itself!
+ unless( ($group->Type || '') eq 'Owner' &&
+ ($group->Domain || '') eq 'RT::Ticket-Role' ) {
+ return $self->SUPER::__DependsOn( %args );
+ }
+
+ # we don't delete group, so we have to fix Ticket and Group
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON | VARIABLE,
+ TargetObjects => $group,
+ Shredder => $args{'Shredder'}
+ );
+ $args{'Shredder'}->PutResolver(
+ BaseClass => ref $self,
+ TargetClass => ref $group,
+ Code => sub {
+ my %args = (@_);
+ my $group = $args{'TargetObject'};
+ return if $args{'Shredder'}->GetState( Object => $group ) & (WIPED|IN_WIPING);
+ return unless ($group->Type || '') eq 'Owner';
+ return unless ($group->Domain || '') eq 'RT::Ticket-Role';
+
+ return if $group->MembersObj->Count > 1;
+
+ my $group_member = $args{'BaseObject'};
+
+ if( $group_member->MemberObj->id == $RT::Nobody->id ) {
+ RT::Shredder::Exception->throw( "Couldn't delete Nobody from owners role group" );
+ }
+
+ my( $status, $msg ) = $group->AddMember( $RT::Nobody->id );
+ RT::Shredder::Exception->throw( $msg ) unless $status;
+
+ my $ticket = RT::Ticket->new( $group->CurrentUser );
+ $ticket->Load( $group->Instance );
+ RT::Shredder::Exception->throw( "Couldn't load ticket" ) unless $ticket->id;
+
+ ( $status, $msg ) = $ticket->_Set( Field => 'Owner',
+ Value => $RT::Nobody->id,
+ );
+ RT::Shredder::Exception->throw( $msg ) unless $status;
+
+ return;
+ },
+ );
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+
+#TODO: If we plan write export tool we also should fetch parent groups
+# now we only wipeout things.
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+ my $obj = $self->MemberObj;
+ if( $obj && $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Principal #". $self->MemberId ." object.";
+ }
+
+ $obj = $self->GroupObj;
+ if( $obj && $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Principal #". $self->GroupId ." object.";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Link.pm b/rt/lib/RT/Shredder/Link.pm
new file mode 100644
index 0000000..18cf4ed
--- /dev/null
+++ b/rt/lib/RT/Shredder/Link.pm
@@ -0,0 +1,140 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Link ();
+package RT::Link;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+use RT::Shredder::Constants;
+
+use RT::Shredder::Transaction;
+use RT::Shredder::Record;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# AddLink transactions
+ my $map = RT::Ticket->LINKTYPEMAP;
+ my $link_meta = $map->{ $self->Type };
+ unless ( $link_meta && $link_meta->{'Mode'} && $link_meta->{'Type'} ) {
+ RT::Shredder::Exception->throw( 'Wrong link link_meta, no record for '. $self->Type );
+ }
+ if ( $self->BaseURI->IsLocal ) {
+ my $objs = $self->BaseObj->Transactions;
+ $objs->Limit(
+ FIELD => 'Type',
+ OPERATOR => '=',
+ VALUE => 'AddLink',
+ );
+ $objs->Limit( FIELD => 'NewValue', VALUE => $self->Target );
+ while ( my ($k, $v) = each %$map ) {
+ next unless $v->{'Type'} eq $link_meta->{'Type'};
+ next unless $v->{'Mode'} eq $link_meta->{'Mode'};
+ $objs->Limit( FIELD => 'Field', VALUE => $k );
+ }
+ push( @$list, $objs );
+ }
+
+ my %reverse = ( Base => 'Target', Target => 'Base' );
+ if ( $self->TargetURI->IsLocal ) {
+ my $objs = $self->TargetObj->Transactions;
+ $objs->Limit(
+ FIELD => 'Type',
+ OPERATOR => '=',
+ VALUE => 'AddLink',
+ );
+ $objs->Limit( FIELD => 'NewValue', VALUE => $self->Base );
+ while ( my ($k, $v) = each %$map ) {
+ next unless $v->{'Type'} eq $link_meta->{'Type'};
+ next unless $v->{'Mode'} eq $reverse{ $link_meta->{'Mode'} };
+ $objs->Limit( FIELD => 'Field', VALUE => $k );
+ }
+ push( @$list, $objs );
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON|WIPE_AFTER,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__DependsOn( %args );
+}
+
+#TODO: Link record has small strength, but should be encountered
+# if we plan write export tool.
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+# FIXME: if link is local then object should exist
+
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/ObjectCustomFieldValue.pm b/rt/lib/RT/Shredder/ObjectCustomFieldValue.pm
new file mode 100644
index 0000000..1d3c977
--- /dev/null
+++ b/rt/lib/RT/Shredder/ObjectCustomFieldValue.pm
@@ -0,0 +1,116 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::ObjectCustomFieldValue ();
+package RT::ObjectCustomFieldValue;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Ticket
+ my $obj = $self->TicketObj;
+ if( defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Ticket #". $self->id ." object";
+ }
+
+# Custom Field
+ $obj = $self->CustomFieldObj;
+ if( defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related CustomField #". $self->id ." object";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/POD.pm b/rt/lib/RT/Shredder/POD.pm
new file mode 100644
index 0000000..a3538b3
--- /dev/null
+++ b/rt/lib/RT/Shredder/POD.pm
@@ -0,0 +1,131 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::POD;
+
+use strict;
+use warnings;
+use Pod::Select;
+
+sub plugin_html
+{
+ my ($file, $out_fh) = @_;
+ my $parser = new RT::Shredder::POD::HTML;
+ $parser->select('ARGUMENTS', 'USAGE');
+ $parser->parse_from_file( $file, $out_fh );
+}
+
+sub plugin_cli
+{
+ my ($file, $out_fh, $no_name) = @_;
+ use Pod::PlainText;
+ local @Pod::PlainText::ISA = ('Pod::Select', @Pod::PlainText::ISA);
+ my $parser = new Pod::PlainText;
+ $parser->select('SYNOPSIS', 'ARGUMENTS', 'USAGE');
+ $parser->add_selection('NAME') unless $no_name;
+ $parser->parse_from_file( $file, $out_fh );
+}
+
+sub shredder_cli
+{
+ my ($file, $out_fh) = @_;
+ use Pod::PlainText;
+ local @Pod::PlainText::ISA = ('Pod::Select', @Pod::PlainText::ISA);
+ my $parser = new Pod::PlainText;
+ $parser->select('NAME', 'SYNOPSIS', 'USAGE', 'OPTIONS');
+ $parser->parse_from_file( $file, $out_fh );
+}
+
+package RT::Shredder::POD::HTML;
+use base qw(Pod::Select);
+
+sub command
+{
+ my( $self, $command, $paragraph, $line_num ) = @_;
+
+ my $tag;
+ if ($command =~ /^head(\d+)$/) { $tag = "h$1" }
+ my $out_fh = $self->output_handle();
+ my $expansion = $self->interpolate($paragraph, $line_num);
+ $expansion =~ s/^\s+|\s+$//;
+
+ print $out_fh "<$tag>" if $tag;
+ print $out_fh $expansion;
+ print $out_fh "</$tag>" if $tag;
+ print $out_fh "\n";
+}
+
+sub verbatim
+{
+ my ($self, $paragraph, $line_num) = @_;
+ my $out_fh = $self->output_handle();
+ print $out_fh "<pre>";
+ print $out_fh $paragraph;
+ print $out_fh "</pre>";
+ print $out_fh "\n";
+}
+
+sub textblock {
+ my ($self, $paragraph, $line_num) = @_;
+ my $out_fh = $self->output_handle();
+ my $expansion = $self->interpolate($paragraph, $line_num);
+ $expansion =~ s/^\s+|\s+$//;
+ print $out_fh "<p>";
+ print $out_fh $expansion;
+ print $out_fh "</p>";
+ print $out_fh "\n";
+}
+
+sub interior_sequence {
+ my ($self, $seq_command, $seq_argument) = @_;
+ ## Expand an interior sequence; sample actions might be:
+ return "<b>$seq_argument</b>" if $seq_command eq 'B';
+ return "<i>$seq_argument</i>" if $seq_command eq 'I';
+ return "<span class=\"pod-sequence-$seq_command\">$seq_argument</span>";
+}
+1;
diff --git a/rt/lib/RT/Shredder/Plugin.pm b/rt/lib/RT/Shredder/Plugin.pm
new file mode 100644
index 0000000..4ba6258
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin.pm
@@ -0,0 +1,249 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin;
+
+use strict;
+use warnings FATAL => 'all';
+use File::Spec ();
+
+=head1 NAME
+
+RT::Shredder::Plugin - interface to access shredder plugins
+
+=head1 SYNOPSIS
+
+ use RT::Shredder::Plugin;
+
+ # get list of the plugins
+ my %plugins = RT::Shredder::Plugin->List;
+
+ # load plugin by name
+ my $plugin = new RT::Shredder::Plugin;
+ my( $status, $msg ) = $plugin->LoadByName( 'Tickets' );
+ unless( $status ) {
+ print STDERR "Couldn't load plugin 'Tickets': $msg\n";
+ exit(1);
+ }
+
+ # load plugin by preformatted string
+ my $plugin = new RT::Shredder::Plugin;
+ my( $status, $msg ) = $plugin->LoadByString( 'Tickets=status,deleted' );
+ unless( $status ) {
+ print STDERR "Couldn't load plugin: $msg\n";
+ exit(1);
+ }
+
+=head1 METHODS
+
+=head2 new
+
+Object constructor, returns new object. Takes optional hash
+as arguments, it's not required and this class doesn't use it,
+but plugins could define some arguments and can handle them
+after your've load it.
+
+=cut
+
+sub new
+{
+ my $proto = shift;
+ my $self = bless( {}, ref $proto || $proto );
+ $self->_Init( @_ );
+ return $self;
+}
+
+sub _Init
+{
+ my $self = shift;
+ my %args = ( @_ );
+ $self->{'opt'} = \%args;
+}
+
+=head2 List
+
+Returns hash with names of the available plugins as keys and path to
+library files as values. Method has no arguments. Can be used as class
+method too.
+
+Takes optional argument C<type> and leaves in the result hash only
+plugins of that type.
+
+=cut
+
+sub List
+{
+ my $self = shift;
+ my $type = shift;
+
+ my @files;
+ foreach my $root( @INC ) {
+ my $mask = File::Spec->catfile( $root, qw(RT Shredder Plugin *.pm) );
+ push @files, glob $mask;
+ }
+
+ my %res = map { $_ =~ m/([^\\\/]+)\.pm$/; $1 => $_ } reverse @files;
+
+ return %res unless $type;
+
+ delete $res{'Base'};
+ foreach my $name( keys %res ) {
+ my $class = join '::', qw(RT Shredder Plugin), $name;
+ unless( eval "require $class" ) {
+ delete $res{ $name };
+ next;
+ }
+ next if lc $class->Type eq lc $type;
+ delete $res{ $name };
+ }
+
+ return %res;
+}
+
+=head2 LoadByName
+
+Takes name of the plugin as first argument, loads plugin,
+creates new plugin object and reblesses self into plugin
+if all steps were successfuly finished, then you don't need to
+create new object for the plugin.
+
+Other arguments are sent to the constructor of the plugin
+(method new.)
+
+Returns C<$status> and C<$message>. On errors status
+is C<false> value.
+
+=cut
+
+sub LoadByName
+{
+ my $self = shift;
+ my $name = shift or return (0, "Name not specified");
+
+ local $@;
+ my $plugin = "RT::Shredder::Plugin::$name";
+ eval "require $plugin" or return( 0, $@ );
+ return( 0, "Plugin '$plugin' has no method new") unless $plugin->can('new');
+
+ my $obj = eval { $plugin->new( @_ ) };
+ return( 0, $@ ) if $@;
+ return( 0, 'constructor returned empty object' ) unless $obj;
+
+ $self->Rebless( $obj );
+ return( 1, "successfuly load plugin" );
+}
+
+=head2 LoadByString
+
+Takes formatted string as first argument and which is used to
+load plugin. The format of the string is
+
+ <plugin name>[=<arg>,<val>[;<arg>,<val>]...]
+
+exactly like in the L<rt-shredder> script. All other
+arguments are sent to the plugins constructor.
+
+Method does the same things as C<LoadByName>, but also
+checks if the plugin supports arguments and values are correct,
+so you can C<Run> specified plugin immediatly.
+
+Returns list with C<$status> and C<$message>. On errors status
+is C<false>.
+
+=cut
+
+sub LoadByString
+{
+ my $self = shift;
+ my ($plugin, $args) = split /=/, ( shift || '' ), 2;
+
+ my ($status, $msg) = $self->LoadByName( $plugin, @_ );
+ return( $status, $msg ) unless $status;
+
+ my %args;
+ foreach( split /\s*;\s*/, ( $args || '' ) ) {
+ my( $k,$v ) = split /\s*,\s*/, ( $_ || '' ), 2;
+ unless( $args{$k} ) {
+ $args{$k} = $v;
+ next;
+ }
+
+ $args{$k} = [ $args{$k} ] unless UNIVERSAL::isa( $args{ $k }, 'ARRAY');
+ push @{ $args{$k} }, $v;
+ }
+
+ ($status, $msg) = $self->HasSupportForArgs( keys %args );
+ return( $status, $msg ) unless $status;
+
+ ($status, $msg) = $self->TestArgs( %args );
+ return( $status, $msg ) unless $status;
+
+ return( 1, "successfuly load plugin" );
+}
+
+=head2 Rebless
+
+Instance method that takes one object as argument and rebless
+the current object into into class of the argument and copy data
+of the former. Returns nothing.
+
+Method is used by C<Load*> methods to automaticaly rebless
+C<RT::Shredder::Plugin> object into class of the loaded
+plugin.
+
+=cut
+
+sub Rebless
+{
+ my( $self, $obj ) = @_;
+ bless( $self, ref $obj );
+ %{$self} = %{$obj};
+ return;
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Plugin/Attachments.pm b/rt/lib/RT/Shredder/Plugin/Attachments.pm
new file mode 100644
index 0000000..36dbbcf
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin/Attachments.pm
@@ -0,0 +1,141 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin::Attachments;
+
+use strict;
+use warnings FATAL => 'all';
+use base qw(RT::Shredder::Plugin::Base::Search);
+
+=head1 NAME
+
+RT::Shredder::Plugin::Attachments - search plugin for wiping attachments.
+
+=head1 ARGUMENTS
+
+=head2 files_only - boolean value
+
+Search only file attachments.
+
+=head2 file - mask
+
+Search files with specific file name only.
+
+Example: '*.xl?' or '*.gif'
+
+=head2 longer - attachment content size
+
+Search attachments which content is longer than specified.
+You can use trailing 'K' or 'M' character to specify size in
+kilobytes or megabytes.
+
+=cut
+
+sub SupportArgs { return $_[0]->SUPER::SupportArgs, qw(files_only file longer) }
+
+sub TestArgs
+{
+ my $self = shift;
+ my %args = @_;
+ my $queue;
+ if( $args{'file'} ) {
+ unless( $args{'file'} =~ /^[\w\. *?]+$/) {
+ return( 0, "Files mask '$args{file}' has invalid characters" );
+ }
+ $args{'file'} = $self->ConvertMaskToSQL( $args{'file'} );
+ }
+ if( $args{'longer'} ) {
+ unless( $args{'longer'} =~ /^\d+\s*[mk]?$/i ) {
+ return( 0, "Invalid file size argument '$args{longer}'" );
+ }
+ }
+ return $self->SUPER::TestArgs( %args );
+}
+
+sub Run
+{
+ my $self = shift;
+ my @conditions = ();
+ my @values = ();
+ if( $self->{'opt'}{'file'} ) {
+ my $mask = $self->{'opt'}{'file'};
+ push @conditions, "( Filename LIKE ? )";
+ push @values, $mask;
+ }
+ if( $self->{'opt'}{'files_only'} ) {
+ push @conditions, "( LENGTH(Filename) > 0 )";
+ }
+ if( $self->{'opt'}{'longer'} ) {
+ my $size = $self->{'opt'}{'longer'};
+ $size =~ s/([mk])//i;
+ $size *= 1024 if $1 && lc $1 eq 'k';
+ $size *= 1024*1024 if $1 && lc $1 eq 'm';
+ push @conditions, "( LENGTH(Content) > ? )";
+ push @values, $size;
+ }
+ return (0, "At least one condition should be provided" ) unless @conditions;
+ my $query = "SELECT id FROM Attachments WHERE ". join ' AND ', @conditions;
+ if( $self->{'opt'}{'limit'} ) {
+ $RT::Handle->ApplyLimits( \$query, $self->{'opt'}{'limit'} );
+ }
+ my $sth = $RT::Handle->SimpleQuery( $query, @values );
+ return (0, "Internal error: '$sth'. Please send bug report.") unless $sth;
+
+ my @objs;
+ while( my $row = $sth->fetchrow_arrayref ) {
+ push @objs, $row->[0];
+ }
+ return (0, "Internal error: '". $sth->err ."'. Please send bug report.") if $sth->err;
+
+ map { $_ = "RT::Attachment-$_" } @objs;
+
+ return (1, @objs);
+}
+
+1;
+
diff --git a/rt/lib/RT/Shredder/Plugin/Base.pm b/rt/lib/RT/Shredder/Plugin/Base.pm
new file mode 100644
index 0000000..3e3e73b
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin/Base.pm
@@ -0,0 +1,188 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin::Base;
+
+use strict;
+use warnings FATAL => 'all';
+
+=head1 NAME
+
+RT::Shredder::Plugin::Base - base class for Shredder plugins.
+
+=cut
+
+sub new
+{
+ my $proto = shift;
+ my $self = bless( {}, ref $proto || $proto );
+ $self->_Init( @_ );
+ return $self;
+}
+
+sub _Init
+{
+ my $self = shift;
+ $self->{'opt'} = { @_ };
+}
+
+=head1 USAGE
+
+=head2 masks
+
+If any argument is marked with keyword C<mask> then it means
+that this argument support two special characters:
+
+1) C<*> matches any non empty sequence of the characters.
+For example C<*@example.com> will match any email address in
+C<example.com> domain.
+
+2) C<?> matches exactly one character.
+For example C<????> will match any string four characters long.
+
+=head1 METHODS
+
+=head2 for subclassing in plugins
+
+=head3 Type - is not supported yet
+
+See F<Todo> for more info.
+
+=cut
+
+sub Type { return '' }
+
+=head3 SupportArgs
+
+Takes nothing.
+Returns list of the supported plugin arguments.
+
+Base class returns list of the arguments which all
+classes B<must> support.
+
+=cut
+
+sub SupportArgs { return () }
+
+=head3 HasSupportForArgs
+
+Takes a list of argument names. Returns true if
+all arguments are supported by plugin and returns
+C<(0, $msg)> in other case.
+
+=cut
+
+sub HasSupportForArgs
+{
+ my $self = shift;
+ my @args = @_;
+ my @unsupported = ();
+ foreach my $a( @args ) {
+ push @unsupported, $a unless grep $_ eq $a, $self->SupportArgs;
+ }
+ return( 1 ) unless @unsupported;
+ return( 0, "Plugin doesn't support argument(s): @unsupported" ) if @unsupported;
+}
+
+=head3 TestArgs
+
+Takes hash with arguments and thier values and returns true
+if all values pass testing otherwise returns C<(0, $msg)>.
+
+Stores arguments hash in C<$self->{'opt'}>, you can access this hash
+from C<Run> method.
+
+Method should be subclassed if plugin support non standard arguments.
+
+=cut
+
+sub TestArgs
+{
+ my $self = shift;
+ my %args = @_;
+ if ( $self->{'opt'} ) {
+ $self->{'opt'} = { %{$self->{'opt'}}, %args };
+ } else {
+ $self->{'opt'} = \%args;
+ }
+ return 1;
+}
+
+=head3 Run
+
+Takes no arguments.
+Executes plugin and return C<(1, @objs)> on success or
+C<(0, $msg)> if error had happenned.
+
+Method B<must> be subclassed, this class always returns error.
+
+Method B<must> be called only after C<TestArgs> method in other
+case values of the arguments are not available.
+
+=cut
+
+sub Run { return (0, "This is abstract plugin, you couldn't use it directly") }
+
+=head2 utils
+
+=head3 ConvertMaskToSQL
+
+Takes one argument - mask with C<*> and C<?> chars and
+return mask SQL chars.
+
+=cut
+
+sub ConvertMaskToSQL {
+ my $self = shift;
+ my $mask = shift || '';
+ $mask =~ s/\*/%/g;
+ $mask =~ s/\?/_/g;
+ return $mask;
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Plugin/Base/Dump.pm b/rt/lib/RT/Shredder/Plugin/Base/Dump.pm
new file mode 100644
index 0000000..ecfc82e
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin/Base/Dump.pm
@@ -0,0 +1,70 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin::Base::Dump;
+
+use strict;
+use warnings FATAL => 'all';
+
+use base qw(RT::Shredder::Plugin::Base);
+
+=head1 NAME
+
+RT::Shredder::Plugin::Base - base class for Shredder plugins.
+
+=cut
+
+sub Type { return 'dump' }
+sub AppliesToStates { return () }
+sub SupportArgs { return () }
+
+sub PushMark { return 1 }
+sub PopMark { return 1 }
+sub RollbackTo { return 1 }
+
+1;
diff --git a/rt/lib/RT/Shredder/Plugin/Base/Search.pm b/rt/lib/RT/Shredder/Plugin/Base/Search.pm
new file mode 100644
index 0000000..21de159
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin/Base/Search.pm
@@ -0,0 +1,142 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin::Base::Search;
+
+use strict;
+use warnings FATAL => 'all';
+
+use base qw(RT::Shredder::Plugin::Base);
+
+=head1 NAME
+
+RT::Shredder::Plugin::Base - base class for Shredder plugins.
+
+=cut
+
+sub Type { return 'search' }
+
+=head1 ARGUMENTS
+
+Arguments which all plugins support.
+
+=head2 limit - unsigned integer
+
+Allow you to limit search results. B<< Default value is C<10> >>.
+
+=head1 METHODS
+
+=cut
+
+sub SupportArgs
+{
+ my %seen;
+ return sort
+ grep $_ && !$seen{$_},
+ shift->SUPER::SupportArgs(@_),
+ qw(limit);
+}
+
+sub TestArgs
+{
+ my $self = shift;
+ my %args = @_;
+ if( defined $args{'limit'} && $args{'limit'} ne '' ) {
+ my $limit = $args{'limit'};
+ $limit =~ s/[^0-9]//g;
+ unless( $args{'limit'} eq $limit ) {
+ return( 0, "'limit' should be an unsigned integer");
+ }
+ $args{'limit'} = $limit;
+ } else {
+ $args{'limit'} = 10;
+ }
+ return $self->SUPER::TestArgs( %args );
+}
+
+sub SetResolvers { return 1 }
+
+
+=head2 FetchNext $collection [, $init]
+
+Returns next object in collection as method L<RT::SearchBuilder/Next>, but
+doesn't stop on page boundaries.
+
+When method is called with true C<$init> arg it enables pages on collection
+and selects first page.
+
+Main purpose of this method is to avoid loading of whole collection into
+memory as RT does by default when pager is not used. This method init paging
+on the collection, but doesn't stop when reach page end.
+
+Example:
+
+ $plugin->FetchNext( $tickets, 'init' );
+ while( my $ticket = $plugin->FetchNext( $tickets ) ) {
+ ...
+ }
+
+=cut
+
+use constant PAGE_SIZE => 100;
+sub FetchNext {
+ my ($self, $objs, $init) = @_;
+ if ( $init ) {
+ $objs->RowsPerPage( PAGE_SIZE );
+ $objs->FirstPage;
+ return;
+ }
+
+ my $obj = $objs->Next;
+ return $obj if $obj;
+ $objs->NextPage;
+ return $objs->Next;
+}
+
+1;
+
diff --git a/rt/lib/RT/Shredder/Plugin/Objects.pm b/rt/lib/RT/Shredder/Plugin/Objects.pm
new file mode 100644
index 0000000..fe2314a
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin/Objects.pm
@@ -0,0 +1,107 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin::Objects;
+
+use strict;
+use warnings FATAL => 'all';
+use base qw(RT::Shredder::Plugin::Base::Search);
+
+use RT::Shredder;
+
+=head1 NAME
+
+RT::Shredder::Plugin::Objects - search plugin for wiping any selected object.
+
+=head1 ARGUMENTS
+
+This plugin searches an RT object you want, so you can use
+the object name as argument and id as value, for example if
+you want select ticket #123 then from CLI you write next
+command:
+
+ rt-shredder --plugin 'Objects=Ticket,123'
+
+=cut
+
+sub SupportArgs
+{
+ return $_[0]->SUPER::SupportArgs, @RT::Shredder::SUPPORTED_OBJECTS;
+}
+
+sub TestArgs
+{
+ my $self = shift;
+ my %args = @_;
+
+ my @strings;
+ foreach my $name( @RT::Shredder::SUPPORTED_OBJECTS ) {
+ next unless $args{$name};
+
+ my $list = $args{$name};
+ $list = [$list] unless UNIVERSAL::isa( $list, 'ARRAY' );
+ push @strings, map "RT::$name\-$_", @$list;
+ }
+
+ my @objs = RT::Shredder->CastObjectsToRecords( Objects => \@strings );
+
+ my @res = $self->SUPER::TestArgs( %args );
+
+ $self->{'opt'}->{'objects'} = \@objs;
+
+ return (@res);
+}
+
+sub Run
+{
+ my $self = shift;
+ my %args = ( Shredder => undef, @_ );
+ return (1, @{$self->{'opt'}->{'objects'}});
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Plugin/SQLDump.pm b/rt/lib/RT/Shredder/Plugin/SQLDump.pm
new file mode 100644
index 0000000..cd3fa33
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin/SQLDump.pm
@@ -0,0 +1,96 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin::SQLDump;
+
+use strict;
+use warnings;
+
+use base qw(RT::Shredder::Plugin::Base::Dump);
+use RT::Shredder;
+
+sub AppliesToStates { return 'after wiping dependencies' }
+
+sub SupportArgs
+{
+ my $self = shift;
+ return $self->SUPER::SupportArgs, qw(file_name from_storage);
+}
+
+sub TestArgs
+{
+ my $self = shift;
+ my %args = @_;
+ $args{'from_storage'} = 1 unless defined $args{'from_storage'};
+ my $file = $args{'file_name'} = RT::Shredder->GetFileName(
+ FileName => $args{'file_name'},
+ FromStorage => delete $args{'from_storage'},
+ );
+ open $args{'file_handle'}, ">:raw", $file
+ or return (0, "Couldn't open '$file' for write: $!");
+
+ return $self->SUPER::TestArgs( %args );
+}
+
+sub FileName { return $_[0]->{'opt'}{'file_name'} }
+sub FileHandle { return $_[0]->{'opt'}{'file_handle'} }
+
+sub Run
+{
+ my $self = shift;
+ return (0, 'no handle') unless my $fh = $self->{'opt'}{'file_handle'};
+
+ my %args = ( Object => undef, @_ );
+ my $query = $args{'Object'}->_AsInsertQuery;
+ $query .= "\n" unless $query =~ /\n$/;
+
+ return print $fh $query or return (0, "Couldn't write to filehandle");
+ return 1;
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Plugin/Summary.pm b/rt/lib/RT/Shredder/Plugin/Summary.pm
new file mode 100644
index 0000000..6ab76ba
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin/Summary.pm
@@ -0,0 +1,188 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin::Summary;
+
+use strict;
+use warnings FATAL => 'all';
+
+use base qw(RT::Shredder::Plugin::SQLDump);
+
+sub AppliesToStates { return 'before any action' }
+
+sub TestArgs
+{
+ my $self = shift;
+ my %args = (file_name => '', @_);
+ unless( $args{'file_name'} ) {
+ require POSIX;
+ $args{'file_name'} = POSIX::strftime( "summary-%Y%m%dT%H%M%S.XXXX.txt", gmtime );
+ }
+ return $self->SUPER::TestArgs( %args );
+}
+
+sub Run
+{
+ my $self = shift;
+ my %args = ( Object => undef, @_ );
+ my $class = ref $args{'Object'};
+ $class =~ s/^RT:://;
+ $class =~ s/:://g;
+ my $method = 'WriteDown'. $class;
+ $method = 'WriteDownDefault' unless $self->can($method);
+ return $self->$method( %args );
+ return 1;
+}
+
+my %skip_refs_to = ();
+
+sub WriteDownDefault {
+ my $self = shift;
+ my %args = ( Object => undef, @_ );
+ return $self->_WriteDownHash(
+ $args{'Object'},
+ $self->_MakeHash( $args{'Object'} ),
+ );
+}
+
+# TODO: cover other objects
+# ACE.pm
+# Attachment.pm
+# CustomField.pm
+# CustomFieldValue.pm
+# GroupMember.pm
+# Group.pm
+# Link.pm
+# ObjectCustomFieldValue.pm
+# Principal.pm
+# Queue.pm
+# Ticket.pm
+# User.pm
+
+# ScripAction.pm - works fine with defaults
+# ScripCondition.pm - works fine with defaults
+# Template.pm - works fine with defaults
+
+sub WriteDownCachedGroupMember { return 1 }
+sub WriteDownPrincipal { return 1 }
+
+sub WriteDownGroup {
+ my $self = shift;
+ my %args = ( Object => undef, @_ );
+ if ( $args{'Object'}->Domain =~ /-Role$/ ) {
+ return $skip_refs_to{ $args{'Object'}->_AsString } = 1;
+ }
+ return $self->WriteDownDefault( %args );
+}
+
+sub WriteDownTransaction {
+ my $self = shift;
+ my %args = ( Object => undef, @_ );
+
+ my $props = $self->_MakeHash( $args{'Object'} );
+ $props->{'Object'} = delete $props->{'ObjectType'};
+ $props->{'Object'} .= '-'. delete $props->{'ObjectId'}
+ if $props->{'ObjectId'};
+ return 1 if $skip_refs_to{ $props->{'Object'} };
+
+ delete $props->{$_} foreach grep
+ !defined $props->{$_} || $props->{$_} eq '', keys %$props;
+
+ return $self->_WriteDownHash( $args{'Object'}, $props );
+}
+
+sub WriteDownScrip {
+ my $self = shift;
+ my %args = ( Object => undef, @_ );
+ my $props = $self->_MakeHash( $args{'Object'} );
+ $props->{'Action'} = $args{'Object'}->ActionObj->Name;
+ $props->{'Condition'} = $args{'Object'}->ConditionObj->Name;
+ $props->{'Template'} = $args{'Object'}->TemplateObj->Name;
+ $props->{'Queue'} = $args{'Object'}->QueueObj->Name || 'global';
+
+ return $self->_WriteDownHash( $args{'Object'}, $props );
+}
+
+sub _MakeHash {
+ my ($self, $obj) = @_;
+ my $hash = $self->__MakeHash( $obj );
+ foreach (grep exists $hash->{$_}, qw(Creator LastUpdatedBy)) {
+ my $method = $_ .'Obj';
+ my $u = $obj->$method();
+ $hash->{ $_ } = $u->EmailAddress || $u->Name || $u->_AsString;
+ }
+ return $hash;
+}
+
+sub __MakeHash {
+ my ($self, $obj) = @_;
+ my %hash;
+ $hash{ $_ } = $obj->$_()
+ foreach sort keys %{ $obj->_ClassAccessible };
+ return \%hash;
+}
+
+sub _WriteDownHash {
+ my ($self, $obj, $hash) = @_;
+ return (0, 'no handle') unless my $fh = $self->{'opt'}{'file_handle'};
+
+ print $fh "=== ". $obj->_AsString ." ===\n"
+ or return (0, "Couldn't write to filehandle");
+
+ foreach my $key( sort keys %$hash ) {
+ my $val = $hash->{ $key };
+ next unless defined $val;
+ $val =~ s/\n/\n /g;
+ print $fh $key .': '. $val ."\n"
+ or return (0, "Couldn't write to filehandle");
+ }
+ print $fh "\n" or return (0, "Couldn't write to filehandle");
+ return 1;
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Plugin/Tickets.pm b/rt/lib/RT/Shredder/Plugin/Tickets.pm
new file mode 100644
index 0000000..ebbfb3e
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin/Tickets.pm
@@ -0,0 +1,153 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin::Tickets;
+
+use strict;
+use warnings FATAL => 'all';
+use base qw(RT::Shredder::Plugin::Base::Search);
+
+=head1 NAME
+
+RT::Shredder::Plugin::Tickets - search plugin for wiping tickets.
+
+=head1 ARGUMENTS
+
+=head2 query - query string
+
+Search tickets with query string.
+Examples:
+ Queue = 'my queue' AND ( Status = 'deleted' OR Status = 'rejected' )
+ LastUpdated < '2003-12-31 23:59:59'
+
+B<Hint:> You can construct query with the query builder in RT's web
+interface and then open advanced page and copy query string.
+
+Arguments C<queue>, C<status> and C<updated_before> have been dropped
+as you can easy make the same search with the C<query> option.
+See examples above.
+
+=head2 with_linked - boolen
+
+Deletes all tickets that are linked to tickets that match L<query>.
+
+=head2 apply_query_to_linked - boolean
+
+Delete linked tickets only if those too match L<query>.
+
+See also L<with_linked>.
+
+=cut
+
+sub SupportArgs { return $_[0]->SUPER::SupportArgs, qw(query with_linked apply_query_to_linked) }
+
+sub TestArgs
+{
+ my $self = shift;
+ my %args = @_;
+ my $queue;
+ if( $args{'query'} ) {
+ my $objs = RT::Tickets->new( $RT::SystemUser );
+ $objs->{'allow_deleted_search'} = 1;
+ my ($status, $msg) = $objs->FromSQL( $args{'query'} );
+ return( 0, "Bad query argument, error: $msg" ) unless $status;
+ $self->{'opt'}{'objects'} = $objs;
+ }
+ $args{'with_linked'} = 1 if $args{'apply_query_to_linked'};
+
+ return $self->SUPER::TestArgs( %args );
+}
+
+sub Run
+{
+ my $self = shift;
+ my $objs = $self->{'opt'}{'objects'}
+ or return (1, undef);
+
+ $objs->OrderByCols( { FIELD => 'id', ORDER => 'ASC' } );
+ unless ( $self->{'opt'}{'with_linked'} ) {
+ if( $self->{'opt'}{'limit'} ) {
+ $objs->RowsPerPage( $self->{'opt'}{'limit'} );
+ }
+ return (1, $objs);
+ }
+
+ my (@top, @linked, %seen);
+ $self->FetchNext($objs, 1);
+ while ( my $obj = $self->FetchNext( $objs ) ) {
+ next if $seen{ $obj->id }++;
+ push @linked, $self->GetLinked( Object => $obj, Seen => \%seen );
+ push @top, $obj;
+ last if $self->{'opt'}{'limit'}
+ && @top >= $self->{'opt'}{'limit'};
+ }
+ return (1, @top, @linked);
+}
+
+sub GetLinked
+{
+ my $self = shift;
+ my %arg = @_;
+ my @res = ();
+ my $query = 'Linked = '. $arg{'Object'}->id;
+ if ( $self->{'opt'}{'apply_query_to_linked'} ) {
+ $query .= " AND ( ". $self->{'opt'}{'query'} ." )";
+ }
+ my $objs = RT::Tickets->new( $RT::SystemUser );
+ $objs->{'allow_deleted_search'} = 1;
+ $objs->FromSQL( $query );
+ $self->FetchNext( $objs, 1 );
+ while ( my $linked_obj = $self->FetchNext( $objs ) ) {
+ next if $arg{'Seen'}->{ $linked_obj->id }++;
+ push @res, $self->GetLinked( %arg, Object => $linked_obj );
+ push @res, $linked_obj;
+ }
+ return @res;
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Plugin/Users.pm b/rt/lib/RT/Shredder/Plugin/Users.pm
new file mode 100644
index 0000000..2565fc5
--- /dev/null
+++ b/rt/lib/RT/Shredder/Plugin/Users.pm
@@ -0,0 +1,260 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+package RT::Shredder::Plugin::Users;
+
+use strict;
+use warnings FATAL => 'all';
+use base qw(RT::Shredder::Plugin::Base::Search);
+
+=head1 NAME
+
+RT::Shredder::Plugin::Users - search plugin for wiping users.
+
+=head1 ARGUMENTS
+
+=head2 status - string
+
+Status argument allow you to limit result set to C<disabled>,
+C<enabled> or C<any> users.
+B<< Default value is C<disabled>. >>
+
+=head2 name - mask
+
+User name mask.
+
+=head2 email - mask
+
+Email address mask.
+
+=head2 member_of - group identifier
+
+Using this option users that are members of a particular group can
+be selected for deletion. Identifier is name of user defined group
+or id of a group, as well C<Privileged> or <unprivileged> can used
+to select people from system groups.
+
+=head2 replace_relations - user identifier
+
+When you delete user there are 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 means 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.
+
+Before RT 3.8.5, users who were watchers of deleted tickets B<will be deleted>
+when this option was enabled. Decision has been made that it's not correct
+and you should either shred these deleted tickets, change watchers or
+explicitly delete user by name or email.
+
+Note that found users still B<may have relations> with other objects,
+for example via Creator or LastUpdatedBy fields, and you most probably
+want to use C<replace_relations> option.
+
+=cut
+
+sub SupportArgs
+{
+ return $_[0]->SUPER::SupportArgs,
+ qw(status name email member_of 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{'member_of'} ) {
+ my $group = RT::Group->new( $RT::SystemUser );
+ if ( $args{'member_of'} =~ /^(Everyone|Privileged|Unprivileged)$/i ) {
+ $group->LoadSystemInternalGroup( $args{'member_of'} );
+ }
+ else {
+ $group->LoadUserDefinedGroup( $args{'member_of'} );
+ }
+ unless ( $group->id ) {
+ return (0, "Couldn't load group '$args{'member_of'}'" );
+ }
+ $args{'member_of'} = $group->id;
+
+ }
+ 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 );
+ # XXX: we want preload only things we need, but later while
+ # logging we need all data, TODO envestigate this
+ # $objs->Columns(qw(id Name EmailAddress Lang Timezone
+ # Creator Created LastUpdated LastUpdatedBy));
+ 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'}{'member_of'} ) {
+ $objs->MemberOfGroup( $self->{'opt'}{'member_of'} );
+ }
+ 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);
+}
+
+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->{'allow_deleted_search'} = 1;
+ $tickets->FromSQL( 'Watcher.id = '. $user->id );
+ # HACK: we may use Count method which counts all records
+ # that match condtion, but we really want to know only that
+ # at least one record exist, so we fetch first row only
+ $tickets->RowsPerPage(1);
+ return !$tickets->First;
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Principal.pm b/rt/lib/RT/Shredder/Principal.pm
new file mode 100644
index 0000000..cc5cd0e
--- /dev/null
+++ b/rt/lib/RT/Shredder/Principal.pm
@@ -0,0 +1,127 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Principal ();
+package RT::Principal;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Exceptions;
+use RT::Shredder::Constants;
+use RT::Shredder::Dependencies;
+
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Group or User
+# Could be wiped allready
+ my $obj = $self->Object;
+ if( defined $obj->id ) {
+ push( @$list, $obj );
+ }
+
+# Access Control List
+ my $objs = RT::ACL->new( $self->CurrentUser );
+ $objs->Limit(
+ FIELD => 'PrincipalId',
+ OPERATOR => '=',
+ VALUE => $self->Id
+ );
+ push( @$list, $objs );
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+ my $obj = $self->Object;
+ if( defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related ". $self->Type ." #". $self->id ." object";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Queue.pm b/rt/lib/RT/Shredder/Queue.pm
new file mode 100644
index 0000000..4ab3815
--- /dev/null
+++ b/rt/lib/RT/Shredder/Queue.pm
@@ -0,0 +1,106 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Queue ();
+package RT::Queue;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Tickets
+ my $objs = RT::Tickets->new( $self->CurrentUser );
+ $objs->{'allow_deleted_search'} = 1;
+ $objs->Limit( FIELD => 'Queue', VALUE => $self->Id );
+ push( @$list, $objs );
+
+# Queue role groups( Cc, AdminCc )
+ $objs = RT::Groups->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'Domain', VALUE => 'RT::Queue-Role' );
+ $objs->Limit( FIELD => 'Instance', VALUE => $self->Id );
+ push( @$list, $objs );
+
+# Scrips
+ $objs = RT::Scrips->new( $self->CurrentUser );
+ $objs->LimitToQueue( $self->id );
+ push( @$list, $objs );
+
+# Templates
+ $objs = $self->Templates;
+ push( @$list, $objs );
+
+# Custom Fields
+ $objs = RT::CustomFields->new( $self->CurrentUser );
+ $objs->LimitToQueue( $self->id );
+ push( @$list, $objs );
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__DependsOn( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Record.pm b/rt/lib/RT/Shredder/Record.pm
new file mode 100644
index 0000000..94208a2
--- /dev/null
+++ b/rt/lib/RT/Shredder/Record.pm
@@ -0,0 +1,273 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Record ();
+package RT::Record;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+
+=head2 _AsString
+
+Returns string in format ClassName-ObjectId.
+
+=cut
+
+sub _AsString { return ref($_[0]) ."-". $_[0]->id }
+
+=head2 _AsInsertQuery
+
+Returns INSERT query string that duplicates current record and
+can be used to insert record back into DB after delete.
+
+=cut
+
+sub _AsInsertQuery
+{
+ my $self = shift;
+
+ my $dbh = $RT::Handle->dbh;
+
+ my $res = "INSERT INTO ". $dbh->quote_identifier( $self->Table );
+ my $values = $self->{'values'};
+ $res .= "(". join( ",", map { $dbh->quote_identifier( $_ ) } sort keys %$values ) .")";
+ $res .= " VALUES";
+ $res .= "(". join( ",", map { $dbh->quote( $values->{$_} ) } sort keys %$values ) .")";
+ $res .= ";";
+
+ return $res;
+}
+
+sub BeforeWipeout { return 1 }
+
+=head2 Dependencies
+
+Returns L<RT::Shredder::Dependencies> object.
+
+=cut
+
+sub Dependencies
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Flags => DEPENDS_ON,
+ @_,
+ );
+
+ unless( $self->id ) {
+ RT::Shredder::Exception->throw('Object is not loaded');
+ }
+
+ my $deps = RT::Shredder::Dependencies->new();
+ if( $args{'Flags'} & DEPENDS_ON ) {
+ $self->__DependsOn( %args, Dependencies => $deps );
+ }
+ if( $args{'Flags'} & RELATES ) {
+ $self->__Relates( %args, Dependencies => $deps );
+ }
+ return $deps;
+}
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Object custom field values
+ my $objs = $self->CustomFieldValues;
+ $objs->{'find_expired_rows'} = 1;
+ push( @$list, $objs );
+
+# Object attributes
+ $objs = $self->Attributes;
+ push( @$list, $objs );
+
+# Transactions
+ $objs = RT::Transactions->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'ObjectType', VALUE => ref $self );
+ $objs->Limit( FIELD => 'ObjectId', VALUE => $self->id );
+ push( @$list, $objs );
+
+# Links
+ if ( $self->can('_Links') ) {
+ # XXX: We don't use Links->Next as it's dies when object
+ # is linked to object that doesn't exist
+ # also, ->Next skip links to deleted tickets :(
+ foreach ( qw(Base Target) ) {
+ my $objs = $self->_Links( $_ );
+ $objs->_DoSearch;
+ push @$list, $objs->ItemsArrayRef;
+ }
+ }
+
+# ACE records
+ $objs = RT::ACL->new( $self->CurrentUser );
+ $objs->LimitToObject( $self );
+ push( @$list, $objs );
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return;
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+ if( $self->_Accessible( 'Creator', 'read' ) ) {
+ my $obj = RT::Principal->new( $self->CurrentUser );
+ $obj->Load( $self->Creator );
+
+ if( $obj && defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ push @{ $rec->{'Description'} },
+ "Have no related User(Creator) #". $self->Creator ." object";
+ }
+ }
+
+ if( $self->_Accessible( 'LastUpdatedBy', 'read' ) ) {
+ my $obj = RT::Principal->new( $self->CurrentUser );
+ $obj->Load( $self->LastUpdatedBy );
+
+ if( $obj && defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ push @{ $rec->{'Description'} },
+ "Have no related User(LastUpdatedBy) #". $self->LastUpdatedBy ." object";
+ }
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+
+ # cause of this $self->SUPER::__Relates should be called last
+ # in overridden subs
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $rec->{'State'} |= VALID unless( $rec->{'State'} & INVALID );
+
+ return;
+}
+
+# implement proxy method because some RT classes
+# override Delete method
+sub __Wipeout
+{
+ my $self = shift;
+ my $msg = $self->_AsString ." wiped out";
+ $self->SUPER::Delete;
+ $RT::Logger->info( $msg );
+ return;
+}
+
+sub ValidateRelations
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ @_
+ );
+ unless( $args{'Shredder'} ) {
+ $args{'Shredder'} = new RT::Shredder();
+ }
+
+ my $rec = $args{'Shredder'}->PutObject( Object => $self );
+ return if( $rec->{'State'} & VALID );
+ $self = $rec->{'Object'};
+
+ $self->_ValidateRelations( %args, Flags => RELATES );
+ $rec->{'State'} |= VALID unless( $rec->{'State'} & INVALID );
+
+ return;
+}
+
+sub _ValidateRelations
+{
+ my $self = shift;
+ my %args = ( @_ );
+
+ my $deps = $self->Dependencies( %args );
+
+ $deps->ValidateRelations( %args );
+
+ return;
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Scrip.pm b/rt/lib/RT/Shredder/Scrip.pm
new file mode 100644
index 0000000..2cf7244
--- /dev/null
+++ b/rt/lib/RT/Shredder/Scrip.pm
@@ -0,0 +1,130 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Scrip ();
+package RT::Scrip;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# No dependencies that should be deleted with record
+# Scrip actions and conditions should be exported in feature with it.
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Queue
+ my $obj = $self->QueueObj;
+ if( defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Queue #". $self->id ." object";
+ }
+
+# Condition
+ $obj = $self->ConditionObj;
+ if( defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related ScripCondition #". $self->id ." object";
+ }
+# Action
+ $obj = $self->ActionObj;
+ if( defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related ScripAction #". $self->id ." object";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/ScripAction.pm b/rt/lib/RT/Shredder/ScripAction.pm
new file mode 100644
index 0000000..9ecfc76
--- /dev/null
+++ b/rt/lib/RT/Shredder/ScripAction.pm
@@ -0,0 +1,100 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::ScripAction ();
+package RT::ScripAction;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Scrips
+ my $objs = RT::Scrips->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'ScripAction', VALUE => $self->Id );
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $objs,
+ Shredder => $args{'Shredder'}
+ );
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# TODO: Check here for exec module
+
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/ScripCondition.pm b/rt/lib/RT/Shredder/ScripCondition.pm
new file mode 100644
index 0000000..48b8620
--- /dev/null
+++ b/rt/lib/RT/Shredder/ScripCondition.pm
@@ -0,0 +1,101 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::ScripCondition ();
+package RT::ScripCondition;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Scrips
+ my $objs = RT::Scrips->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'ScripCondition', VALUE => $self->Id );
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $objs,
+ Shredder => $args{'Shredder'}
+ );
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# TODO: Check here for exec module
+
+ return $self->SUPER::__Relates( %args );
+}
+
+
+1;
diff --git a/rt/lib/RT/Shredder/Template.pm b/rt/lib/RT/Shredder/Template.pm
new file mode 100644
index 0000000..db2c4c7
--- /dev/null
+++ b/rt/lib/RT/Shredder/Template.pm
@@ -0,0 +1,120 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Template ();
+package RT::Template;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Scrips
+ my $objs = RT::Scrips->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'Template', VALUE => $self->Id );
+ push( @$list, $objs );
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'},
+ );
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Queue
+ my $obj = $self->QueueObj;
+ if( $obj && defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Queue #". $self->id ." object";
+ }
+
+# TODO: Users(Creator, LastUpdatedBy)
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Ticket.pm b/rt/lib/RT/Shredder/Ticket.pm
new file mode 100644
index 0000000..cacae24
--- /dev/null
+++ b/rt/lib/RT/Shredder/Ticket.pm
@@ -0,0 +1,126 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Ticket ();
+package RT::Ticket;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Tickets which were merged in
+ my $objs = RT::Tickets->new( $self->CurrentUser );
+ $objs->{'allow_deleted_search'} = 1;
+ $objs->Limit( FIELD => 'EffectiveId', VALUE => $self->Id );
+ $objs->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->Id );
+ push( @$list, $objs );
+
+# Ticket role groups( Owner, Requestors, Cc, AdminCc )
+ $objs = RT::Groups->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'Domain', VALUE => 'RT::Ticket-Role' );
+ $objs->Limit( FIELD => 'Instance', VALUE => $self->Id );
+ push( @$list, $objs );
+
+#TODO: Users, Queues if we wish export tool
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Queue
+ my $obj = $self->QueueObj;
+ if( $obj && defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Queue #". $self->Queue ." object";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/Transaction.pm b/rt/lib/RT/Shredder/Transaction.pm
new file mode 100644
index 0000000..cdd6c3e
--- /dev/null
+++ b/rt/lib/RT/Shredder/Transaction.pm
@@ -0,0 +1,115 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::Transaction ();
+package RT::Transaction;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Attachments
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $self->Attachments,
+ Shredder => $args{'Shredder'}
+ );
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Ticket
+ my $obj = $self->TicketObj;
+ if( $obj && defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Ticket #". $self->id ." object";
+ }
+
+# TODO: Users(Creator, LastUpdatedBy)
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+
+1;
diff --git a/rt/lib/RT/Shredder/User.pm b/rt/lib/RT/Shredder/User.pm
new file mode 100644
index 0000000..d17860a
--- /dev/null
+++ b/rt/lib/RT/Shredder/User.pm
@@ -0,0 +1,191 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@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 }}}
+
+use RT::User ();
+package RT::User;
+
+use strict;
+use warnings;
+use warnings FATAL => 'redefine';
+
+use RT::Shredder::Constants;
+use RT::Shredder::Exceptions;
+use RT::Shredder::Dependencies;
+
+my @OBJECTS = qw(
+ Attachments
+ CachedGroupMembers
+ CustomFields
+ CustomFieldValues
+ GroupMembers
+ Groups
+ Links
+ Principals
+ Queues
+ ScripActions
+ ScripConditions
+ Scrips
+ Templates
+ ObjectCustomFieldValues
+ Tickets
+ Transactions
+ Users
+);
+
+sub __DependsOn
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Principal
+ $deps->_PushDependency(
+ BaseObject => $self,
+ Flags => DEPENDS_ON | WIPE_AFTER,
+ TargetObject => $self->PrincipalObj,
+ Shredder => $args{'Shredder'}
+ );
+
+# ACL equivalence group
+# don't use LoadACLEquivalenceGroup cause it may not exists any more
+ my $objs = RT::Groups->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'Domain', VALUE => 'ACLEquivalence' );
+ $objs->Limit( FIELD => 'Instance', VALUE => $self->Id );
+ push( @$list, $objs );
+
+# Cleanup user's membership
+ $objs = RT::GroupMembers->new( $self->CurrentUser );
+ $objs->Limit( FIELD => 'MemberId', VALUE => $self->Id );
+ push( @$list, $objs );
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+
+# TODO: Almost all objects has Creator, LastUpdatedBy and etc. fields
+# which are references on users(Principal actualy)
+ my @var_objs;
+ foreach( @OBJECTS ) {
+ my $class = "RT::$_";
+ foreach my $method ( qw(Creator LastUpdatedBy) ) {
+ my $objs = $class->new( $self->CurrentUser );
+ next unless $objs->NewItem->_Accessible( $method => 'read' );
+ $objs->Limit( FIELD => $method, VALUE => $self->id );
+ push @var_objs, $objs;
+ }
+ }
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => DEPENDS_ON | VARIABLE,
+ TargetObjects => \@var_objs,
+ Shredder => $args{'Shredder'}
+ );
+
+ return $self->SUPER::__DependsOn( %args );
+}
+
+sub __Relates
+{
+ my $self = shift;
+ my %args = (
+ Shredder => undef,
+ Dependencies => undef,
+ @_,
+ );
+ my $deps = $args{'Dependencies'};
+ my $list = [];
+
+# Principal
+ my $obj = $self->PrincipalObj;
+ if( $obj && defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related ACL equivalence Group object";
+ }
+
+ $obj = RT::Group->new( $RT::SystemUser );
+ $obj->LoadACLEquivalenceGroup( $self->PrincipalObj );
+ if( $obj && defined $obj->id ) {
+ push( @$list, $obj );
+ } else {
+ my $rec = $args{'Shredder'}->GetRecord( Object => $self );
+ $self = $rec->{'Object'};
+ $rec->{'State'} |= INVALID;
+ $rec->{'Description'} = "Have no related Principal #". $self->id ." object";
+ }
+
+ $deps->_PushDependencies(
+ BaseObject => $self,
+ Flags => RELATES,
+ TargetObjects => $list,
+ Shredder => $args{'Shredder'}
+ );
+ return $self->SUPER::__Relates( %args );
+}
+
+sub BeforeWipeout
+{
+ my $self = shift;
+ if( $self->Name =~ /^(RT_System|Nobody)$/ ) {
+ RT::Shredder::Exception::Info->throw('SystemObject');
+ }
+ return $self->SUPER::BeforeWipeout( @_ );
+}
+
+1;