]>
Commit | Line | Data |
---|---|---|
84fb5b46 MKG |
1 | # BEGIN BPS TAGGED BLOCK {{{ |
2 | # | |
3 | # COPYRIGHT: | |
4 | # | |
320f0092 | 5 | # This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC |
84fb5b46 MKG |
6 | # <sales@bestpractical.com> |
7 | # | |
8 | # (Except where explicitly superseded by other copyright notices) | |
9 | # | |
10 | # | |
11 | # LICENSE: | |
12 | # | |
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 | |
16 | # from www.gnu.org. | |
17 | # | |
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. | |
22 | # | |
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. | |
28 | # | |
29 | # | |
30 | # CONTRIBUTION SUBMISSION POLICY: | |
31 | # | |
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.) | |
37 | # | |
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. | |
46 | # | |
47 | # END BPS TAGGED BLOCK }}} | |
48 | ||
49 | package RT::CachedGroupMember; | |
50 | ||
51 | use strict; | |
52 | use warnings; | |
53 | ||
54 | ||
55 | use base 'RT::Record'; | |
56 | ||
57 | sub Table {'CachedGroupMembers'} | |
58 | ||
59 | =head1 NAME | |
60 | ||
61 | RT::CachedGroupMember | |
62 | ||
63 | =head1 SYNOPSIS | |
64 | ||
65 | use RT::CachedGroupMember; | |
66 | ||
67 | =head1 DESCRIPTION | |
68 | ||
69 | =head1 METHODS | |
70 | ||
71 | =cut | |
72 | ||
73 | # {{ Create | |
74 | ||
75 | =head2 Create PARAMHASH | |
76 | ||
77 | Create takes a hash of values and creates a row in the database: | |
78 | ||
79 | 'Group' is the "top level" group we're building the cache for. This | |
80 | is an RT::Principal object | |
81 | ||
82 | 'Member' is the RT::Principal of the user or group we're adding to | |
83 | the cache. | |
84 | ||
85 | 'ImmediateParent' is the RT::Principal of the group that this | |
86 | principal belongs to to get here | |
87 | ||
88 | int(11) 'Via' is an internal reference to CachedGroupMembers->Id of | |
89 | the "parent" record of this cached group member. It should be empty if | |
90 | this member is a "direct" member of this group. (In that case, it will | |
91 | be set to this cached group member's id after creation) | |
92 | ||
93 | This routine should _only_ be called by GroupMember->Create | |
94 | ||
95 | =cut | |
96 | ||
97 | sub Create { | |
98 | my $self = shift; | |
99 | my %args = ( Group => '', | |
100 | Member => '', | |
101 | ImmediateParent => '', | |
102 | Via => '0', | |
103 | Disabled => '0', | |
104 | @_ ); | |
105 | ||
106 | unless ( $args{'Member'} | |
107 | && UNIVERSAL::isa( $args{'Member'}, 'RT::Principal' ) | |
108 | && $args{'Member'}->Id ) { | |
109 | $RT::Logger->debug("$self->Create: bogus Member argument"); | |
110 | } | |
111 | ||
112 | unless ( $args{'Group'} | |
113 | && UNIVERSAL::isa( $args{'Group'}, 'RT::Principal' ) | |
114 | && $args{'Group'}->Id ) { | |
115 | $RT::Logger->debug("$self->Create: bogus Group argument"); | |
116 | } | |
117 | ||
118 | unless ( $args{'ImmediateParent'} | |
119 | && UNIVERSAL::isa( $args{'ImmediateParent'}, 'RT::Principal' ) | |
120 | && $args{'ImmediateParent'}->Id ) { | |
121 | $RT::Logger->debug("$self->Create: bogus ImmediateParent argument"); | |
122 | } | |
123 | ||
124 | # If the parent group for this group member is disabled, it's disabled too, along with all its children | |
125 | if ( $args{'ImmediateParent'}->Disabled ) { | |
126 | $args{'Disabled'} = $args{'ImmediateParent'}->Disabled; | |
127 | } | |
128 | ||
129 | my $id = $self->SUPER::Create( | |
130 | GroupId => $args{'Group'}->Id, | |
131 | MemberId => $args{'Member'}->Id, | |
132 | ImmediateParentId => $args{'ImmediateParent'}->Id, | |
133 | Disabled => $args{'Disabled'}, | |
134 | Via => $args{'Via'}, ); | |
135 | ||
136 | unless ($id) { | |
137 | $RT::Logger->warning( "Couldn't create " | |
138 | . $args{'Member'} | |
139 | . " as a cached member of " | |
140 | . $args{'Group'}->Id . " via " | |
141 | . $args{'Via'} ); | |
142 | return (undef); #this will percolate up and bail out of the transaction | |
143 | } | |
144 | if ( $self->__Value('Via') == 0 ) { | |
145 | my ( $vid, $vmsg ) = $self->__Set( Field => 'Via', Value => $id ); | |
146 | unless ($vid) { | |
147 | $RT::Logger->warning( "Due to a via error, couldn't create " | |
148 | . $args{'Member'} | |
149 | . " as a cached member of " | |
150 | . $args{'Group'}->Id . " via " | |
151 | . $args{'Via'} ); | |
152 | return (undef) | |
153 | ; #this will percolate up and bail out of the transaction | |
154 | } | |
155 | } | |
156 | ||
157 | return $id if $args{'Member'}->id == $args{'Group'}->id; | |
158 | ||
159 | if ( $args{'Member'}->IsGroup() ) { | |
160 | my $GroupMembers = $args{'Member'}->Object->MembersObj(); | |
161 | while ( my $member = $GroupMembers->Next() ) { | |
162 | my $cached_member = | |
163 | RT::CachedGroupMember->new( $self->CurrentUser ); | |
164 | my $c_id = $cached_member->Create( | |
165 | Group => $args{'Group'}, | |
166 | Member => $member->MemberObj, | |
167 | ImmediateParent => $args{'Member'}, | |
168 | Disabled => $args{'Disabled'}, | |
169 | Via => $id ); | |
170 | unless ($c_id) { | |
171 | return (undef); #percolate the error upwards. | |
172 | # the caller will log an error and abort the transaction | |
173 | } | |
174 | ||
175 | } | |
176 | } | |
177 | ||
178 | return ($id); | |
179 | ||
180 | } | |
181 | ||
182 | ||
183 | ||
184 | =head2 Delete | |
185 | ||
186 | Deletes the current CachedGroupMember from the group it's in and cascades | |
187 | the delete to all submembers. This routine could be completely excised if | |
188 | mysql supported foreign keys with cascading deletes. | |
189 | ||
190 | =cut | |
191 | ||
192 | sub Delete { | |
193 | my $self = shift; | |
194 | ||
195 | ||
196 | my $member = $self->MemberObj(); | |
197 | if ( $member->IsGroup ) { | |
198 | my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser ); | |
199 | ||
200 | $deletable->Limit( FIELD => 'id', | |
201 | OPERATOR => '!=', | |
202 | VALUE => $self->id ); | |
203 | $deletable->Limit( FIELD => 'Via', | |
204 | OPERATOR => '=', | |
205 | VALUE => $self->id ); | |
206 | ||
207 | while ( my $kid = $deletable->Next ) { | |
208 | my $kid_err = $kid->Delete(); | |
209 | unless ($kid_err) { | |
210 | $RT::Logger->error( | |
211 | "Couldn't delete CachedGroupMember " . $kid->Id ); | |
212 | return (undef); | |
213 | } | |
214 | } | |
215 | } | |
216 | my $ret = $self->SUPER::Delete(); | |
217 | unless ($ret) { | |
218 | $RT::Logger->error( "Couldn't delete CachedGroupMember " . $self->Id ); | |
219 | return (undef); | |
220 | } | |
221 | return $ret; | |
222 | } | |
223 | ||
224 | ||
225 | ||
226 | =head2 SetDisabled | |
227 | ||
228 | SetDisableds the current CachedGroupMember from the group it's in and cascades | |
229 | the SetDisabled to all submembers. This routine could be completely excised if | |
230 | mysql supported foreign keys with cascading SetDisableds. | |
231 | ||
232 | =cut | |
233 | ||
234 | sub SetDisabled { | |
235 | my $self = shift; | |
236 | my $val = shift; | |
237 | ||
238 | # if it's already disabled, we're good. | |
239 | return (1) if ( $self->__Value('Disabled') == $val); | |
240 | my $err = $self->_Set(Field => 'Disabled', Value => $val); | |
241 | my ($retval, $msg) = $err->as_array(); | |
242 | unless ($retval) { | |
243 | $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $self->Id .": $msg"); | |
244 | return ($err); | |
245 | } | |
246 | ||
247 | my $member = $self->MemberObj(); | |
248 | if ( $member->IsGroup ) { | |
249 | my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser ); | |
250 | ||
251 | $deletable->Limit( FIELD => 'Via', OPERATOR => '=', VALUE => $self->id ); | |
252 | $deletable->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->id ); | |
253 | ||
254 | while ( my $kid = $deletable->Next ) { | |
255 | my $kid_err = $kid->SetDisabled($val ); | |
256 | unless ($kid_err) { | |
257 | $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $kid->Id ); | |
258 | return ($kid_err); | |
259 | } | |
260 | } | |
261 | } | |
262 | return ($err); | |
263 | } | |
264 | ||
265 | ||
266 | ||
267 | =head2 GroupObj | |
268 | ||
269 | Returns the RT::Principal object for this group Group | |
270 | ||
271 | =cut | |
272 | ||
273 | sub GroupObj { | |
274 | my $self = shift; | |
275 | my $principal = RT::Principal->new( $self->CurrentUser ); | |
276 | $principal->Load( $self->GroupId ); | |
277 | return ($principal); | |
278 | } | |
279 | ||
280 | ||
281 | ||
282 | =head2 ImmediateParentObj | |
283 | ||
284 | Returns the RT::Principal object for this group ImmediateParent | |
285 | ||
286 | =cut | |
287 | ||
288 | sub ImmediateParentObj { | |
289 | my $self = shift; | |
290 | my $principal = RT::Principal->new( $self->CurrentUser ); | |
291 | $principal->Load( $self->ImmediateParentId ); | |
292 | return ($principal); | |
293 | } | |
294 | ||
295 | ||
296 | ||
297 | =head2 MemberObj | |
298 | ||
299 | Returns the RT::Principal object for this group member | |
300 | ||
301 | =cut | |
302 | ||
303 | sub MemberObj { | |
304 | my $self = shift; | |
305 | my $principal = RT::Principal->new( $self->CurrentUser ); | |
306 | $principal->Load( $self->MemberId ); | |
307 | return ($principal); | |
308 | } | |
309 | ||
310 | # }}} | |
311 | ||
312 | ||
313 | ||
314 | ||
315 | ||
316 | ||
317 | =head2 id | |
318 | ||
319 | Returns the current value of id. | |
320 | (In the database, id is stored as int(11).) | |
321 | ||
322 | ||
323 | =cut | |
324 | ||
325 | ||
326 | =head2 GroupId | |
327 | ||
328 | Returns the current value of GroupId. | |
329 | (In the database, GroupId is stored as int(11).) | |
330 | ||
331 | ||
332 | ||
333 | =head2 SetGroupId VALUE | |
334 | ||
335 | ||
336 | Set GroupId to VALUE. | |
337 | Returns (1, 'Status message') on success and (0, 'Error Message') on failure. | |
338 | (In the database, GroupId will be stored as a int(11).) | |
339 | ||
340 | ||
341 | =cut | |
342 | ||
343 | ||
344 | =head2 MemberId | |
345 | ||
346 | Returns the current value of MemberId. | |
347 | (In the database, MemberId is stored as int(11).) | |
348 | ||
349 | ||
350 | ||
351 | =head2 SetMemberId VALUE | |
352 | ||
353 | ||
354 | Set MemberId to VALUE. | |
355 | Returns (1, 'Status message') on success and (0, 'Error Message') on failure. | |
356 | (In the database, MemberId will be stored as a int(11).) | |
357 | ||
358 | ||
359 | =cut | |
360 | ||
361 | ||
362 | =head2 Via | |
363 | ||
364 | Returns the current value of Via. | |
365 | (In the database, Via is stored as int(11).) | |
366 | ||
367 | ||
368 | ||
369 | =head2 SetVia VALUE | |
370 | ||
371 | ||
372 | Set Via to VALUE. | |
373 | Returns (1, 'Status message') on success and (0, 'Error Message') on failure. | |
374 | (In the database, Via will be stored as a int(11).) | |
375 | ||
376 | ||
377 | =cut | |
378 | ||
379 | ||
380 | =head2 ImmediateParentId | |
381 | ||
382 | Returns the current value of ImmediateParentId. | |
383 | (In the database, ImmediateParentId is stored as int(11).) | |
384 | ||
385 | ||
386 | ||
387 | =head2 SetImmediateParentId VALUE | |
388 | ||
389 | ||
390 | Set ImmediateParentId to VALUE. | |
391 | Returns (1, 'Status message') on success and (0, 'Error Message') on failure. | |
392 | (In the database, ImmediateParentId will be stored as a int(11).) | |
393 | ||
394 | ||
395 | =cut | |
396 | ||
397 | ||
398 | =head2 Disabled | |
399 | ||
400 | Returns the current value of Disabled. | |
401 | (In the database, Disabled is stored as smallint(6).) | |
402 | ||
403 | ||
404 | ||
405 | =head2 SetDisabled VALUE | |
406 | ||
407 | ||
408 | Set Disabled to VALUE. | |
409 | Returns (1, 'Status message') on success and (0, 'Error Message') on failure. | |
410 | (In the database, Disabled will be stored as a smallint(6).) | |
411 | ||
412 | ||
413 | =cut | |
414 | ||
415 | ||
416 | ||
417 | sub _CoreAccessible { | |
418 | { | |
419 | ||
420 | id => | |
af59614d | 421 | {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, |
84fb5b46 | 422 | GroupId => |
af59614d | 423 | {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, |
84fb5b46 | 424 | MemberId => |
af59614d | 425 | {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, |
84fb5b46 | 426 | Via => |
af59614d | 427 | {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, |
84fb5b46 | 428 | ImmediateParentId => |
af59614d | 429 | {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, |
84fb5b46 | 430 | Disabled => |
af59614d | 431 | {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'}, |
84fb5b46 MKG |
432 | |
433 | } | |
434 | }; | |
435 | ||
af59614d MKG |
436 | sub Serialize { |
437 | die "CachedGroupMembers should never be serialized"; | |
438 | } | |
439 | ||
84fb5b46 MKG |
440 | RT::Base->_ImportOverlays(); |
441 | ||
442 | 1; |