1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2005 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., 675 Mass Ave, Cambridge, MA 02139, USA.
28 # CONTRIBUTION SUBMISSION POLICY:
30 # (The following paragraph is not intended to limit the rights granted
31 # to you to modify and distribute this software under the terms of
32 # the GNU General Public License and is only of importance to you if
33 # you choose to contribute your changes and enhancements to the
34 # community by submitting them to Best Practical Solutions, LLC.)
36 # By intentionally submitting any modifications, corrections or
37 # derivatives to this work, or any other work intended for use with
38 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
39 # you are the copyright holder for those contributions and you grant
40 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
41 # royalty-free, perpetual, license to use, copy, create derivative
42 # works based on those contributions, and sublicense and distribute
43 # those contributions and any derivatives thereof.
45 # END BPS TAGGED BLOCK }}}
46 package RT::CachedGroupMember;
49 no warnings qw(redefine);
57 use RT::CachedGroupMember;
67 =head2 Create PARAMHASH
69 Create takes a hash of values and creates a row in the database:
71 'Group' is the "top level" group we're building the cache for. This
72 is an RT::Principal object
74 'Member' is the RT::Principal of the user or group we're adding to
77 'ImmediateParent' is the RT::Principal of the group that this
78 principal belongs to to get here
80 int(11) 'Via' is an internal reference to CachedGroupMembers->Id of
81 the "parent" record of this cached group member. It should be empty if
82 this member is a "direct" member of this group. (In that case, it will
83 be set to this cached group member's id after creation)
85 This routine should _only_ be called by GroupMember->Create
91 my %args = ( Group => '',
93 ImmediateParent => '',
98 unless ( $args{'Member'}
99 && UNIVERSAL::isa( $args{'Member'}, 'RT::Principal' )
100 && $args{'Member'}->Id ) {
101 $RT::Logger->debug("$self->Create: bogus Member argument");
104 unless ( $args{'Group'}
105 && UNIVERSAL::isa( $args{'Group'}, 'RT::Principal' )
106 && $args{'Group'}->Id ) {
107 $RT::Logger->debug("$self->Create: bogus Group argument");
110 unless ( $args{'ImmediateParent'}
111 && UNIVERSAL::isa( $args{'ImmediateParent'}, 'RT::Principal' )
112 && $args{'ImmediateParent'}->Id ) {
113 $RT::Logger->debug("$self->Create: bogus ImmediateParent argument");
116 # If the parent group for this group member is disabled, it's disabled too, along with all its children
117 if ( $args{'ImmediateParent'}->Disabled ) {
118 $args{'Disabled'} = $args{'ImmediateParent'}->Disabled;
121 my $id = $self->SUPER::Create(
122 GroupId => $args{'Group'}->Id,
123 MemberId => $args{'Member'}->Id,
124 ImmediateParentId => $args{'ImmediateParent'}->Id,
125 Disabled => $args{'Disabled'},
126 Via => $args{'Via'}, );
129 $RT::Logger->warning( "Couldn't create "
131 . " as a cached member of "
132 . $args{'Group'}->Id . " via "
134 return (undef); #this will percolate up and bail out of the transaction
136 if ( $self->__Value('Via') == 0 ) {
137 my ( $vid, $vmsg ) = $self->__Set( Field => 'Via', Value => $id );
139 $RT::Logger->warning( "Due to a via error, couldn't create "
141 . " as a cached member of "
142 . $args{'Group'}->Id . " via "
145 ; #this will percolate up and bail out of the transaction
149 if ( $args{'Member'}->IsGroup() ) {
150 my $GroupMembers = $args{'Member'}->Object->MembersObj();
151 while ( my $member = $GroupMembers->Next() ) {
153 RT::CachedGroupMember->new( $self->CurrentUser );
154 my $c_id = $cached_member->Create(
155 Group => $args{'Group'},
156 Member => $member->MemberObj,
157 ImmediateParent => $args{'Member'},
158 Disabled => $args{'Disabled'},
161 return (undef); #percolate the error upwards.
162 # the caller will log an error and abort the transaction
178 Deletes the current CachedGroupMember from the group it's in and cascades
179 the delete to all submembers. This routine could be completely excised if
180 mysql supported foreign keys with cascading deletes.
188 my $member = $self->MemberObj();
189 if ( $member->IsGroup ) {
190 my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
192 $deletable->Limit( FIELD => 'id',
194 VALUE => $self->id );
195 $deletable->Limit( FIELD => 'Via',
197 VALUE => $self->id );
199 while ( my $kid = $deletable->Next ) {
200 my $kid_err = $kid->Delete();
203 "Couldn't delete CachedGroupMember " . $kid->Id );
208 my $err = $self->SUPER::Delete();
210 $RT::Logger->error( "Couldn't delete CachedGroupMember " . $self->Id );
214 # Unless $self->GroupObj still has the member recursively $self->MemberObj
215 # (Since we deleted the database row above, $self no longer counts)
216 unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
219 # Find all ACEs granted to $self->GroupId
220 my $acl = RT::ACL->new($RT::SystemUser);
221 $acl->LimitToPrincipal( Id => $self->GroupId );
224 while ( my $this_ace = $acl->Next() ) {
225 # Find all ACEs which $self-MemberObj has delegated from $this_ace
226 my $delegations = RT::ACL->new($RT::SystemUser);
227 $delegations->DelegatedFrom( Id => $this_ace->Id );
228 $delegations->DelegatedBy( Id => $self->MemberId );
230 # For each delegation
231 while ( my $delegation = $delegations->Next ) {
233 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
235 $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
250 SetDisableds the current CachedGroupMember from the group it's in and cascades
251 the SetDisabled to all submembers. This routine could be completely excised if
252 mysql supported foreign keys with cascading SetDisableds.
260 # if it's already disabled, we're good.
261 return {1} if ($self->__Value('Disabled') == $val);
262 my $err = $self->SUPER::SetDisabled($val);
263 my ($retval, $msg) = $err->as_array();
265 $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $self->Id .": $msg");
269 my $member = $self->MemberObj();
270 if ( $member->IsGroup ) {
271 my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
273 $deletable->Limit( FIELD => 'Via', OPERATOR => '=', VALUE => $self->id );
274 $deletable->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->id );
276 while ( my $kid = $deletable->Next ) {
277 my $kid_err = $kid->SetDisabled($val );
279 $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $kid->Id );
285 # Unless $self->GroupObj still has the member recursively $self->MemberObj
286 # (Since we SetDisabledd the database row above, $self no longer counts)
287 unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
288 # Find all ACEs granted to $self->GroupId
289 my $acl = RT::ACL->new($RT::SystemUser);
290 $acl->LimitToPrincipal( Id => $self->GroupId );
292 while ( my $this_ace = $acl->Next() ) {
293 # Find all ACEs which $self-MemberObj has delegated from $this_ace
294 my $delegations = RT::ACL->new($RT::SystemUser);
295 $delegations->DelegatedFrom( Id => $this_ace->Id );
296 $delegations->DelegatedBy( Id => $self->MemberId );
298 # For each delegation, blow away the delegation
299 while ( my $delegation = $delegations->Next ) {
301 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
303 $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
318 Returns the RT::Principal object for this group Group
324 my $principal = RT::Principal->new( $self->CurrentUser );
325 $principal->Load( $self->GroupId );
331 # {{{ ImmediateParentObj
333 =head2 ImmediateParentObj
335 Returns the RT::Principal object for this group ImmediateParent
339 sub ImmediateParentObj {
341 my $principal = RT::Principal->new( $self->CurrentUser );
342 $principal->Load( $self->ImmediateParentId );
352 Returns the RT::Principal object for this group member
358 my $principal = RT::Principal->new( $self->CurrentUser );
359 $principal->Load( $self->MemberId );