+Both Group and Member are expected to be RT::Principal objects
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Group => undef,
+ Member => undef,
+ InsideTransaction => undef,
+ @_
+ );
+
+ unless ($args{'Group'} &&
+ UNIVERSAL::isa($args{'Group'}, 'RT::Principal') &&
+ $args{'Group'}->Id ) {
+
+ $RT::Logger->warning("GroupMember::Create called with a bogus Group arg");
+ return (undef);
+ }
+
+ unless($args{'Group'}->IsGroup) {
+ $RT::Logger->warning("Someone tried to add a member to a user instead of a group");
+ return (undef);
+ }
+
+ unless ($args{'Member'} &&
+ UNIVERSAL::isa($args{'Member'}, 'RT::Principal') &&
+ $args{'Member'}->Id) {
+ $RT::Logger->warning("GroupMember::Create called with a bogus Principal arg");
+ return (undef);
+ }
+
+
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ # TODO what about the groups key cache?
+ RT::Principal->InvalidateACLCache();
+
+ $RT::Handle->BeginTransaction() unless ($args{'InsideTransaction'});
+
+ # We really need to make sure we don't add any members to this group
+ # that contain the group itself. that would, um, suck.
+ # (and recurse infinitely) Later, we can add code to check this in the
+ # cache and bail so we can support cycling directed graphs
+
+ if ($args{'Member'}->IsGroup) {
+ my $member_object = $args{'Member'}->Object;
+ if ($member_object->HasMemberRecursively($args{'Group'})) {
+ $RT::Logger->debug("Adding that group would create a loop");
+ $RT::Handle->Rollback() unless ($args{'InsideTransaction'});
+ return(undef);
+ }
+ elsif ( $args{'Member'}->Id == $args{'Group'}->Id) {
+ $RT::Logger->debug("Can't add a group to itself");
+ $RT::Handle->Rollback() unless ($args{'InsideTransaction'});
+ return(undef);
+ }
+ }
+
+
+ my $id = $self->SUPER::Create(
+ GroupId => $args{'Group'}->Id,
+ MemberId => $args{'Member'}->Id
+ );
+
+ unless ($id) {
+ $RT::Handle->Rollback() unless ($args{'InsideTransaction'});
+ return (undef);
+ }
+
+ my $cached_member = RT::CachedGroupMember->new( $self->CurrentUser );
+ my $cached_id = $cached_member->Create(
+ Member => $args{'Member'},
+ Group => $args{'Group'},
+ ImmediateParent => $args{'Group'},
+ Via => '0'
+ );
+
+
+ #When adding a member to a group, we need to go back
+ #and popuplate the CachedGroupMembers of all the groups that group is part of .
+
+ my $cgm = RT::CachedGroupMembers->new( $self->CurrentUser );
+
+ # find things which have the current group as a member.
+ # $group is an RT::Principal for the group.
+ $cgm->LimitToGroupsWithMember( $args{'Group'}->Id );
+ $cgm->Limit(
+ SUBCLAUSE => 'filter', # dont't mess up with prev condition
+ FIELD => 'MemberId',
+ OPERATOR => '!=',
+ VALUE => 'main.GroupId',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'AND',
+ );
+
+ while ( my $parent_member = $cgm->Next ) {
+ my $parent_id = $parent_member->MemberId;
+ my $via = $parent_member->Id;
+ my $group_id = $parent_member->GroupId;
+
+ my $other_cached_member =
+ RT::CachedGroupMember->new( $self->CurrentUser );
+ my $other_cached_id = $other_cached_member->Create(
+ Member => $args{'Member'},
+ Group => $parent_member->GroupObj,
+ ImmediateParent => $parent_member->MemberObj,
+ Via => $parent_member->Id
+ );
+ unless ($other_cached_id) {
+ $RT::Logger->err( "Couldn't add " . $args{'Member'}
+ . " as a submember of a supergroup" );
+ $RT::Handle->Rollback() unless ($args{'InsideTransaction'});
+ return (undef);
+ }
+ }
+
+ unless ($cached_id) {
+ $RT::Handle->Rollback() unless ($args{'InsideTransaction'});
+ return (undef);
+ }
+
+ $RT::Handle->Commit() unless ($args{'InsideTransaction'});
+
+ return ($id);