import of rt 3.0.4
[freeside.git] / rt / lib / RT / CachedGroupMember_Overlay.pm
1 # BEGIN LICENSE BLOCK
2
3 # Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
4
5 # (Except where explictly superceded by other copyright notices)
6
7 # This work is made available to you under the terms of Version 2 of
8 # the GNU General Public License. A copy of that license should have
9 # been provided with this software, but in any event can be snarfed
10 # from www.gnu.org.
11
12 # This work is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # General Public License for more details.
16
17 # Unless otherwise specified, all modifications, corrections or
18 # extensions to this work which alter its source code become the
19 # property of Best Practical Solutions, LLC when submitted for
20 # inclusion in the work.
21
22
23 # END LICENSE BLOCK
24 use strict;
25 no warnings qw(redefine);
26
27 # {{{ Create
28
29 =item Create PARAMHASH
30
31 Create takes a hash of values and creates a row in the database:
32
33   'Group' is the "top level" group we're building the cache for. This is an 
34   RT::Principal object
35
36   'Member' is the RT::Principal  of the user or group we're adding
37   to the cache.
38
39   'ImmediateParent' is the RT::Principal of the group that this principal
40   belongs to to get here
41
42   int(11) 'Via' is an internal reference to CachedGroupMembers->Id of
43   the "parent" record of this cached group member. It should be empty if this
44   member is a "direct" member of this group. (In that case, it will be set to this 
45   cached group member's id after creation)
46
47   This routine should _only_ be called by GroupMember->Create
48
49 =cut
50
51 sub Create {
52     my $self = shift;
53     my %args = ( Group           => '',
54                  Member          => '',
55                  ImmediateParent => '',
56                  Via             => '0',
57                  Disabled        => '0',
58                  @_ );
59
60     unless (    $args{'Member'}
61              && UNIVERSAL::isa( $args{'Member'}, 'RT::Principal' )
62              && $args{'Member'}->Id ) {
63         $RT::Logger->debug("$self->Create: bogus Member argument");
64     }
65
66     unless (    $args{'Group'}
67              && UNIVERSAL::isa( $args{'Group'}, 'RT::Principal' )
68              && $args{'Group'}->Id ) {
69         $RT::Logger->debug("$self->Create: bogus Group argument");
70     }
71
72     unless (    $args{'ImmediateParent'}
73              && UNIVERSAL::isa( $args{'ImmediateParent'}, 'RT::Principal' )
74              && $args{'ImmediateParent'}->Id ) {
75         $RT::Logger->debug("$self->Create: bogus ImmediateParent argument");
76     }
77
78     # If the parent group for this group member is disabled, it's disabled too, along with all its children
79     if ( $args{'ImmediateParent'}->Disabled ) {
80         $args{'Disabled'} = $args{'ImmediateParent'}->Disabled;
81     }
82
83     my $id = $self->SUPER::Create(
84                               GroupId           => $args{'Group'}->Id,
85                               MemberId          => $args{'Member'}->Id,
86                               ImmediateParentId => $args{'ImmediateParent'}->Id,
87                               Disabled          => $args{'Disabled'},
88                               Via               => $args{'Via'}, );
89
90     unless ($id) {
91         $RT::Logger->warn( "Couldn't create "
92                            . $args{'Member'}
93                            . " as a cached member of "
94                            . $args{'Group'}->Id . " via "
95                            . $args{'Via'} );
96         return (undef);  #this will percolate up and bail out of the transaction
97     }
98     if ( $self->__Value('Via') == 0 ) {
99         my ( $vid, $vmsg ) = $self->__Set( Field => 'Via', Value => $id );
100         unless ($vid) {
101             $RT::Logger->warn( "Due to a via error, couldn't create "
102                                . $args{'Member'}
103                                . " as a cached member of "
104                                . $args{'Group'}->Id . " via "
105                                . $args{'Via'} );
106             return (undef)
107               ;          #this will percolate up and bail out of the transaction
108         }
109     }
110
111     if ( $args{'Member'}->IsGroup() ) {
112         my $GroupMembers = $args{'Member'}->Object->MembersObj();
113         while ( my $member = $GroupMembers->Next() ) {
114             my $cached_member =
115               RT::CachedGroupMember->new( $self->CurrentUser );
116             my $c_id = $cached_member->Create(
117                                              Group  => $args{'Group'},
118                                              Member => $member->MemberObj,
119                                              ImmediateParent => $args{'Member'},
120                                              Disabled => $args{'Disabled'},
121                                              Via      => $id );
122             unless ($c_id) {
123                 return (undef);    #percolate the error upwards.
124                      # the caller will log an error and abort the transaction
125             }
126
127         }
128     }
129
130     return ($id);
131
132 }
133
134 # }}}
135
136 # {{{ Delete
137
138 =head2 Delete
139
140 Deletes the current CachedGroupMember from the group it's in and cascades 
141 the delete to all submembers. This routine could be completely excised if
142 mysql supported foreign keys with cascading deletes.
143
144 =cut 
145
146 sub Delete {
147     my $self = shift;
148
149     
150     my $member = $self->MemberObj();
151     if ( $member->IsGroup ) {
152         my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
153
154         $deletable->Limit( FIELD    => 'id',
155                            OPERATOR => '!=',
156                            VALUE    => $self->id );
157         $deletable->Limit( FIELD    => 'Via',
158                            OPERATOR => '=',
159                            VALUE    => $self->id );
160
161         while ( my $kid = $deletable->Next ) {
162             my $kid_err = $kid->Delete();
163             unless ($kid_err) {
164                 $RT::Logger->error(
165                               "Couldn't delete CachedGroupMember " . $kid->Id );
166                 return (undef);
167             }
168         }
169     }
170     my $err = $self->SUPER::Delete();
171     unless ($err) {
172         $RT::Logger->error( "Couldn't delete CachedGroupMember " . $self->Id );
173         return (undef);
174     }
175
176     # Unless $self->GroupObj still has the member recursively $self->MemberObj
177     # (Since we deleted the database row above, $self no longer counts)
178     unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
179
180
181         #   Find all ACEs granted to $self->GroupId
182         my $acl = RT::ACL->new($RT::SystemUser);
183         $acl->LimitToPrincipal( Id => $self->GroupId );
184
185
186         while ( my $this_ace = $acl->Next() ) {
187             #       Find all ACEs which $self-MemberObj has delegated from $this_ace
188             my $delegations = RT::ACL->new($RT::SystemUser);
189             $delegations->DelegatedFrom( Id => $this_ace->Id );
190             $delegations->DelegatedBy( Id => $self->MemberId );
191
192             # For each delegation 
193             while ( my $delegation = $delegations->Next ) {
194                 # WHACK IT
195                 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
196                 unless ($del_ret) {
197                     $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
198                     return(undef);
199                 }
200             }
201         }
202     }
203     return ($err);
204 }
205
206 # }}}
207
208 # {{{ SetDisabled
209
210 =head2 SetDisabled
211
212 SetDisableds the current CachedGroupMember from the group it's in and cascades 
213 the SetDisabled to all submembers. This routine could be completely excised if
214 mysql supported foreign keys with cascading SetDisableds.
215
216 =cut 
217
218 sub SetDisabled {
219     my $self = shift;
220     my $val = shift;
221     
222     my $err = $self->SUPER::SetDisabled($val);
223     unless ($err) {
224         $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $self->Id );
225         return (undef);
226     }
227     
228     my $member = $self->MemberObj();
229     if ( $member->IsGroup ) {
230         my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
231
232         $deletable->Limit( FIELD    => 'Via', OPERATOR => '=', VALUE    => $self->id );
233         $deletable->Limit( FIELD    => 'id', OPERATOR => '!=', VALUE    => $self->id );
234
235         while ( my $kid = $deletable->Next ) {
236             my $kid_err = $kid->SetDisabled($val );
237             unless ($kid_err) {
238                 $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $kid->Id );
239                 return (undef);
240             }
241         }
242     }
243
244     # Unless $self->GroupObj still has the member recursively $self->MemberObj
245     # (Since we SetDisabledd the database row above, $self no longer counts)
246     unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
247         #   Find all ACEs granted to $self->GroupId
248         my $acl = RT::ACL->new($RT::SystemUser);
249         $acl->LimitToPrincipal( Id => $self->GroupId );
250
251         while ( my $this_ace = $acl->Next() ) {
252             #       Find all ACEs which $self-MemberObj has delegated from $this_ace
253             my $delegations = RT::ACL->new($RT::SystemUser);
254             $delegations->DelegatedFrom( Id => $this_ace->Id );
255             $delegations->DelegatedBy( Id => $self->MemberId );
256
257             # For each delegation,  blow away the delegation
258             while ( my $delegation = $delegations->Next ) {
259                 # WHACK IT
260                 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
261                 unless ($del_ret) {
262                     $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
263                     return(undef);
264                 }
265             }
266         }
267     }
268     return ($err);
269 }
270
271 # }}}
272
273 # {{{ GroupObj
274
275 =head2 GroupObj  
276
277 Returns the RT::Principal object for this group Group
278
279 =cut
280
281 sub GroupObj {
282     my $self      = shift;
283     my $principal = RT::Principal->new( $self->CurrentUser );
284     $principal->Load( $self->GroupId );
285     return ($principal);
286 }
287
288 # }}}
289
290 # {{{ ImmediateParentObj
291
292 =head2 ImmediateParentObj  
293
294 Returns the RT::Principal object for this group ImmediateParent
295
296 =cut
297
298 sub ImmediateParentObj {
299     my $self      = shift;
300     my $principal = RT::Principal->new( $self->CurrentUser );
301     $principal->Load( $self->ImmediateParentId );
302     return ($principal);
303 }
304
305 # }}}
306
307 # {{{ MemberObj
308
309 =head2 MemberObj  
310
311 Returns the RT::Principal object for this group member
312
313 =cut
314
315 sub MemberObj {
316     my $self      = shift;
317     my $principal = RT::Principal->new( $self->CurrentUser );
318     $principal->Load( $self->MemberId );
319     return ($principal);
320 }
321
322 # }}}
323 1;