1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
6 # <sales@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/licenses/old-licenses/gpl-2.0.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 }}}
49 package RT::CachedGroupMember;
52 no warnings qw(redefine);
60 use RT::CachedGroupMember;
70 =head2 Create PARAMHASH
72 Create takes a hash of values and creates a row in the database:
74 'Group' is the "top level" group we're building the cache for. This
75 is an RT::Principal object
77 'Member' is the RT::Principal of the user or group we're adding to
80 'ImmediateParent' is the RT::Principal of the group that this
81 principal belongs to to get here
83 int(11) 'Via' is an internal reference to CachedGroupMembers->Id of
84 the "parent" record of this cached group member. It should be empty if
85 this member is a "direct" member of this group. (In that case, it will
86 be set to this cached group member's id after creation)
88 This routine should _only_ be called by GroupMember->Create
94 my %args = ( Group => '',
96 ImmediateParent => '',
101 unless ( $args{'Member'}
102 && UNIVERSAL::isa( $args{'Member'}, 'RT::Principal' )
103 && $args{'Member'}->Id ) {
104 $RT::Logger->debug("$self->Create: bogus Member argument");
107 unless ( $args{'Group'}
108 && UNIVERSAL::isa( $args{'Group'}, 'RT::Principal' )
109 && $args{'Group'}->Id ) {
110 $RT::Logger->debug("$self->Create: bogus Group argument");
113 unless ( $args{'ImmediateParent'}
114 && UNIVERSAL::isa( $args{'ImmediateParent'}, 'RT::Principal' )
115 && $args{'ImmediateParent'}->Id ) {
116 $RT::Logger->debug("$self->Create: bogus ImmediateParent argument");
119 # If the parent group for this group member is disabled, it's disabled too, along with all its children
120 if ( $args{'ImmediateParent'}->Disabled ) {
121 $args{'Disabled'} = $args{'ImmediateParent'}->Disabled;
124 my $id = $self->SUPER::Create(
125 GroupId => $args{'Group'}->Id,
126 MemberId => $args{'Member'}->Id,
127 ImmediateParentId => $args{'ImmediateParent'}->Id,
128 Disabled => $args{'Disabled'},
129 Via => $args{'Via'}, );
132 $RT::Logger->warning( "Couldn't create "
134 . " as a cached member of "
135 . $args{'Group'}->Id . " via "
137 return (undef); #this will percolate up and bail out of the transaction
139 if ( $self->__Value('Via') == 0 ) {
140 my ( $vid, $vmsg ) = $self->__Set( Field => 'Via', Value => $id );
142 $RT::Logger->warning( "Due to a via error, couldn't create "
144 . " as a cached member of "
145 . $args{'Group'}->Id . " via "
148 ; #this will percolate up and bail out of the transaction
152 return $id if $args{'Member'}->id == $args{'Group'}->id;
154 if ( $args{'Member'}->IsGroup() ) {
155 my $GroupMembers = $args{'Member'}->Object->MembersObj();
156 while ( my $member = $GroupMembers->Next() ) {
158 RT::CachedGroupMember->new( $self->CurrentUser );
159 my $c_id = $cached_member->Create(
160 Group => $args{'Group'},
161 Member => $member->MemberObj,
162 ImmediateParent => $args{'Member'},
163 Disabled => $args{'Disabled'},
166 return (undef); #percolate the error upwards.
167 # the caller will log an error and abort the transaction
183 Deletes the current CachedGroupMember from the group it's in and cascades
184 the delete to all submembers. This routine could be completely excised if
185 mysql supported foreign keys with cascading deletes.
193 my $member = $self->MemberObj();
194 if ( $member->IsGroup ) {
195 my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
197 $deletable->Limit( FIELD => 'id',
199 VALUE => $self->id );
200 $deletable->Limit( FIELD => 'Via',
202 VALUE => $self->id );
204 while ( my $kid = $deletable->Next ) {
205 my $kid_err = $kid->Delete();
208 "Couldn't delete CachedGroupMember " . $kid->Id );
213 my $err = $self->SUPER::Delete();
215 $RT::Logger->error( "Couldn't delete CachedGroupMember " . $self->Id );
219 # Unless $self->GroupObj still has the member recursively $self->MemberObj
220 # (Since we deleted the database row above, $self no longer counts)
221 unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberId ) ) {
224 # Find all ACEs granted to $self->GroupId
225 my $acl = RT::ACL->new($RT::SystemUser);
226 $acl->LimitToPrincipal( Id => $self->GroupId );
229 while ( my $this_ace = $acl->Next() ) {
230 # Find all ACEs which $self-MemberObj has delegated from $this_ace
231 my $delegations = RT::ACL->new($RT::SystemUser);
232 $delegations->DelegatedFrom( Id => $this_ace->Id );
233 $delegations->DelegatedBy( Id => $self->MemberId );
235 # For each delegation
236 while ( my $delegation = $delegations->Next ) {
238 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
240 $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
255 SetDisableds the current CachedGroupMember from the group it's in and cascades
256 the SetDisabled to all submembers. This routine could be completely excised if
257 mysql supported foreign keys with cascading SetDisableds.
265 # if it's already disabled, we're good.
266 return (1) if ( $self->__Value('Disabled') == $val);
267 my $err = $self->SUPER::SetDisabled($val);
268 my ($retval, $msg) = $err->as_array();
270 $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $self->Id .": $msg");
274 my $member = $self->MemberObj();
275 if ( $member->IsGroup ) {
276 my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
278 $deletable->Limit( FIELD => 'Via', OPERATOR => '=', VALUE => $self->id );
279 $deletable->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->id );
281 while ( my $kid = $deletable->Next ) {
282 my $kid_err = $kid->SetDisabled($val );
284 $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $kid->Id );
290 # Unless $self->GroupObj still has the member recursively $self->MemberObj
291 # (Since we SetDisabledd the database row above, $self no longer counts)
292 unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberId ) ) {
293 # Find all ACEs granted to $self->GroupId
294 my $acl = RT::ACL->new($RT::SystemUser);
295 $acl->LimitToPrincipal( Id => $self->GroupId );
297 while ( my $this_ace = $acl->Next() ) {
298 # Find all ACEs which $self-MemberObj has delegated from $this_ace
299 my $delegations = RT::ACL->new($RT::SystemUser);
300 $delegations->DelegatedFrom( Id => $this_ace->Id );
301 $delegations->DelegatedBy( Id => $self->MemberId );
303 # For each delegation, blow away the delegation
304 while ( my $delegation = $delegations->Next ) {
306 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
308 $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
323 Returns the RT::Principal object for this group Group
329 my $principal = RT::Principal->new( $self->CurrentUser );
330 $principal->Load( $self->GroupId );
336 # {{{ ImmediateParentObj
338 =head2 ImmediateParentObj
340 Returns the RT::Principal object for this group ImmediateParent
344 sub ImmediateParentObj {
346 my $principal = RT::Principal->new( $self->CurrentUser );
347 $principal->Load( $self->ImmediateParentId );
357 Returns the RT::Principal object for this group member
363 my $principal = RT::Principal->new( $self->CurrentUser );
364 $principal->Load( $self->MemberId );