1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
6 # <jesse@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/copyleft/gpl.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
48 package RT::CachedGroupMember;
51 no warnings qw(redefine);
59 use RT::CachedGroupMember;
69 =head2 Create PARAMHASH
71 Create takes a hash of values and creates a row in the database:
73 'Group' is the "top level" group we're building the cache for. This
74 is an RT::Principal object
76 'Member' is the RT::Principal of the user or group we're adding to
79 'ImmediateParent' is the RT::Principal of the group that this
80 principal belongs to to get here
82 int(11) 'Via' is an internal reference to CachedGroupMembers->Id of
83 the "parent" record of this cached group member. It should be empty if
84 this member is a "direct" member of this group. (In that case, it will
85 be set to this cached group member's id after creation)
87 This routine should _only_ be called by GroupMember->Create
93 my %args = ( Group => '',
95 ImmediateParent => '',
100 unless ( $args{'Member'}
101 && UNIVERSAL::isa( $args{'Member'}, 'RT::Principal' )
102 && $args{'Member'}->Id ) {
103 $RT::Logger->debug("$self->Create: bogus Member argument");
106 unless ( $args{'Group'}
107 && UNIVERSAL::isa( $args{'Group'}, 'RT::Principal' )
108 && $args{'Group'}->Id ) {
109 $RT::Logger->debug("$self->Create: bogus Group argument");
112 unless ( $args{'ImmediateParent'}
113 && UNIVERSAL::isa( $args{'ImmediateParent'}, 'RT::Principal' )
114 && $args{'ImmediateParent'}->Id ) {
115 $RT::Logger->debug("$self->Create: bogus ImmediateParent argument");
118 # If the parent group for this group member is disabled, it's disabled too, along with all its children
119 if ( $args{'ImmediateParent'}->Disabled ) {
120 $args{'Disabled'} = $args{'ImmediateParent'}->Disabled;
123 my $id = $self->SUPER::Create(
124 GroupId => $args{'Group'}->Id,
125 MemberId => $args{'Member'}->Id,
126 ImmediateParentId => $args{'ImmediateParent'}->Id,
127 Disabled => $args{'Disabled'},
128 Via => $args{'Via'}, );
131 $RT::Logger->warning( "Couldn't create "
133 . " as a cached member of "
134 . $args{'Group'}->Id . " via "
136 return (undef); #this will percolate up and bail out of the transaction
138 if ( $self->__Value('Via') == 0 ) {
139 my ( $vid, $vmsg ) = $self->__Set( Field => 'Via', Value => $id );
141 $RT::Logger->warning( "Due to a via error, couldn't create "
143 . " as a cached member of "
144 . $args{'Group'}->Id . " via "
147 ; #this will percolate up and bail out of the transaction
151 if ( $args{'Member'}->IsGroup() ) {
152 my $GroupMembers = $args{'Member'}->Object->MembersObj();
153 while ( my $member = $GroupMembers->Next() ) {
155 RT::CachedGroupMember->new( $self->CurrentUser );
156 my $c_id = $cached_member->Create(
157 Group => $args{'Group'},
158 Member => $member->MemberObj,
159 ImmediateParent => $args{'Member'},
160 Disabled => $args{'Disabled'},
163 return (undef); #percolate the error upwards.
164 # the caller will log an error and abort the transaction
180 Deletes the current CachedGroupMember from the group it's in and cascades
181 the delete to all submembers. This routine could be completely excised if
182 mysql supported foreign keys with cascading deletes.
190 my $member = $self->MemberObj();
191 if ( $member->IsGroup ) {
192 my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
194 $deletable->Limit( FIELD => 'id',
196 VALUE => $self->id );
197 $deletable->Limit( FIELD => 'Via',
199 VALUE => $self->id );
201 while ( my $kid = $deletable->Next ) {
202 my $kid_err = $kid->Delete();
205 "Couldn't delete CachedGroupMember " . $kid->Id );
210 my $err = $self->SUPER::Delete();
212 $RT::Logger->error( "Couldn't delete CachedGroupMember " . $self->Id );
216 # Unless $self->GroupObj still has the member recursively $self->MemberObj
217 # (Since we deleted the database row above, $self no longer counts)
218 unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
221 # Find all ACEs granted to $self->GroupId
222 my $acl = RT::ACL->new($RT::SystemUser);
223 $acl->LimitToPrincipal( Id => $self->GroupId );
226 while ( my $this_ace = $acl->Next() ) {
227 # Find all ACEs which $self-MemberObj has delegated from $this_ace
228 my $delegations = RT::ACL->new($RT::SystemUser);
229 $delegations->DelegatedFrom( Id => $this_ace->Id );
230 $delegations->DelegatedBy( Id => $self->MemberId );
232 # For each delegation
233 while ( my $delegation = $delegations->Next ) {
235 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
237 $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
252 SetDisableds the current CachedGroupMember from the group it's in and cascades
253 the SetDisabled to all submembers. This routine could be completely excised if
254 mysql supported foreign keys with cascading SetDisableds.
262 # if it's already disabled, we're good.
263 return {1} if ($self->__Value('Disabled') == $val);
264 my $err = $self->SUPER::SetDisabled($val);
265 my ($retval, $msg) = $err->as_array();
267 $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $self->Id .": $msg");
271 my $member = $self->MemberObj();
272 if ( $member->IsGroup ) {
273 my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
275 $deletable->Limit( FIELD => 'Via', OPERATOR => '=', VALUE => $self->id );
276 $deletable->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->id );
278 while ( my $kid = $deletable->Next ) {
279 my $kid_err = $kid->SetDisabled($val );
281 $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $kid->Id );
287 # Unless $self->GroupObj still has the member recursively $self->MemberObj
288 # (Since we SetDisabledd the database row above, $self no longer counts)
289 unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
290 # Find all ACEs granted to $self->GroupId
291 my $acl = RT::ACL->new($RT::SystemUser);
292 $acl->LimitToPrincipal( Id => $self->GroupId );
294 while ( my $this_ace = $acl->Next() ) {
295 # Find all ACEs which $self-MemberObj has delegated from $this_ace
296 my $delegations = RT::ACL->new($RT::SystemUser);
297 $delegations->DelegatedFrom( Id => $this_ace->Id );
298 $delegations->DelegatedBy( Id => $self->MemberId );
300 # For each delegation, blow away the delegation
301 while ( my $delegation = $delegations->Next ) {
303 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
305 $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
320 Returns the RT::Principal object for this group Group
326 my $principal = RT::Principal->new( $self->CurrentUser );
327 $principal->Load( $self->GroupId );
333 # {{{ ImmediateParentObj
335 =head2 ImmediateParentObj
337 Returns the RT::Principal object for this group ImmediateParent
341 sub ImmediateParentObj {
343 my $principal = RT::Principal->new( $self->CurrentUser );
344 $principal->Load( $self->ImmediateParentId );
354 Returns the RT::Principal object for this group member
360 my $principal = RT::Principal->new( $self->CurrentUser );
361 $principal->Load( $self->MemberId );