rt 4.2.15
[freeside.git] / rt / lib / RT / CachedGroupMember.pm
index fc3e1bf..821b619 100644 (file)
-# BEGIN LICENSE BLOCK
-# 
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-# 
-# (Except where explictly superceded by other copyright notices)
-# 
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2018 Best Practical Solutions, LLC
+#                                          <sales@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
 # This work is made available to you under the terms of Version 2 of
 # the GNU General Public License. A copy of that license should have
 # been provided with this software, but in any event can be snarfed
 # from www.gnu.org.
-# 
+#
 # This work is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # General Public License for more details.
-# 
-# Unless otherwise specified, all modifications, corrections or
-# extensions to this work which alter its source code become the
-# property of Best Practical Solutions, LLC when submitted for
-# inclusion in the work.
-# 
-# 
-# END LICENSE BLOCK
-# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
-# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.  
-# 
-# !! DO NOT EDIT THIS FILE !!
 #
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+package RT::CachedGroupMember;
 
 use strict;
+use warnings;
 
 
-=head1 NAME
+use base 'RT::Record';
 
-RT::CachedGroupMember
+sub Table {'CachedGroupMembers'}
 
+=head1 NAME
+
+  RT::CachedGroupMember
 
 =head1 SYNOPSIS
 
+  use RT::CachedGroupMember;
+
 =head1 DESCRIPTION
 
 =head1 METHODS
 
 =cut
 
-package RT::CachedGroupMember;
-use RT::Record; 
+# {{ Create
+
+=head2 Create PARAMHASH
 
+Create takes a hash of values and creates a row in the database:
+
+  'Group' is the "top level" group we're building the cache for. This 
+  is an RT::Principal object
+
+  'Member' is the RT::Principal  of the user or group we're adding to 
+  the cache.
+
+  'ImmediateParent' is the RT::Principal of the group that this 
+  principal belongs to to get here
+
+  int(11) 'Via' is an internal reference to CachedGroupMembers->Id of
+  the "parent" record of this cached group member. It should be empty if 
+  this member is a "direct" member of this group. (In that case, it will 
+  be set to this cached group member's id after creation)
 
-use vars qw( @ISA );
-@ISA= qw( RT::Record );
+  This routine should _only_ be called by GroupMember->Create
 
-sub _Init {
-  my $self = shift; 
+=cut
+
+sub Create {
+    my $self = shift;
+    my %args = ( Group           => '',
+                 Member          => '',
+                 ImmediateParent => '',
+                 Via             => '0',
+                 Disabled        => '0',
+                 @_ );
+
+    unless (    $args{'Member'}
+             && UNIVERSAL::isa( $args{'Member'}, 'RT::Principal' )
+             && $args{'Member'}->Id ) {
+        $RT::Logger->debug("$self->Create: bogus Member argument");
+    }
+
+    unless (    $args{'Group'}
+             && UNIVERSAL::isa( $args{'Group'}, 'RT::Principal' )
+             && $args{'Group'}->Id ) {
+        $RT::Logger->debug("$self->Create: bogus Group argument");
+    }
+
+    unless (    $args{'ImmediateParent'}
+             && UNIVERSAL::isa( $args{'ImmediateParent'}, 'RT::Principal' )
+             && $args{'ImmediateParent'}->Id ) {
+        $RT::Logger->debug("$self->Create: bogus ImmediateParent argument");
+    }
+
+    # If the parent group for this group member is disabled, it's disabled too, along with all its children
+    if ( $args{'ImmediateParent'}->Disabled ) {
+        $args{'Disabled'} = $args{'ImmediateParent'}->Disabled;
+    }
+
+    my $id = $self->SUPER::Create(
+                              GroupId           => $args{'Group'}->Id,
+                              MemberId          => $args{'Member'}->Id,
+                              ImmediateParentId => $args{'ImmediateParent'}->Id,
+                              Disabled          => $args{'Disabled'},
+                              Via               => $args{'Via'}, );
+
+    unless ($id) {
+        $RT::Logger->warning( "Couldn't create "
+                           . $args{'Member'}
+                           . " as a cached member of "
+                           . $args{'Group'}->Id . " via "
+                           . $args{'Via'} );
+        return (undef);  #this will percolate up and bail out of the transaction
+    }
+    if ( $self->__Value('Via') == 0 ) {
+        my ( $vid, $vmsg ) = $self->__Set( Field => 'Via', Value => $id );
+        unless ($vid) {
+            $RT::Logger->warning( "Due to a via error, couldn't create "
+                               . $args{'Member'}
+                               . " as a cached member of "
+                               . $args{'Group'}->Id . " via "
+                               . $args{'Via'} );
+            return (undef)
+              ;          #this will percolate up and bail out of the transaction
+        }
+    }
+
+    return $id if $args{'Member'}->id == $args{'Group'}->id;
+
+    if ( $args{'Member'}->IsGroup() ) {
+        my $GroupMembers = $args{'Member'}->Object->MembersObj();
+        while ( my $member = $GroupMembers->Next() ) {
+            my $cached_member =
+              RT::CachedGroupMember->new( $self->CurrentUser );
+            my $c_id = $cached_member->Create(
+                                             Group  => $args{'Group'},
+                                             Member => $member->MemberObj,
+                                             ImmediateParent => $args{'Member'},
+                                             Disabled => $args{'Disabled'},
+                                             Via      => $id );
+            unless ($c_id) {
+                return (undef);    #percolate the error upwards.
+                     # the caller will log an error and abort the transaction
+            }
+
+        }
+    }
+
+    return ($id);
 
-  $self->Table('CachedGroupMembers');
-  $self->SUPER::_Init(@_);
 }
 
 
 
+=head2 Delete
 
+Deletes the current CachedGroupMember from the group it's in and cascades 
+the delete to all submembers. This routine could be completely excised if
+mysql supported foreign keys with cascading deletes.
 
-=item Create PARAMHASH
+=cut 
 
-Create takes a hash of values and creates a row in the database:
+sub Delete {
+    my $self = shift;
 
-  int(11) 'GroupId'.
-  int(11) 'MemberId'.
-  int(11) 'Via'.
-  int(11) 'ImmediateParentId'.
-  smallint(6) 'Disabled'.
+    
+    my $member = $self->MemberObj();
+    if ( $member->IsGroup ) {
+        my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
+
+        $deletable->Limit( FIELD    => 'id',
+                           OPERATOR => '!=',
+                           VALUE    => $self->id );
+        $deletable->Limit( FIELD    => 'Via',
+                           OPERATOR => '=',
+                           VALUE    => $self->id );
+
+        while ( my $kid = $deletable->Next ) {
+            my $kid_err = $kid->Delete();
+            unless ($kid_err) {
+                $RT::Logger->error(
+                              "Couldn't delete CachedGroupMember " . $kid->Id );
+                return (undef);
+            }
+        }
+    }
+    my $ret = $self->SUPER::Delete();
+    unless ($ret) {
+        $RT::Logger->error( "Couldn't delete CachedGroupMember " . $self->Id );
+        return (undef);
+    }
+    return $ret;
+}
 
-=cut
 
 
+=head2 SetDisabled
 
+SetDisableds the current CachedGroupMember from the group it's in and cascades 
+the SetDisabled to all submembers. This routine could be completely excised if
+mysql supported foreign keys with cascading SetDisableds.
 
-sub Create {
+=cut 
+
+sub SetDisabled {
     my $self = shift;
-    my %args = ( 
-                GroupId => '',
-                MemberId => '',
-                Via => '',
-                ImmediateParentId => '',
-                Disabled => '0',
-
-                 @_);
-    $self->SUPER::Create(
-                         GroupId => $args{'GroupId'},
-                         MemberId => $args{'MemberId'},
-                         Via => $args{'Via'},
-                         ImmediateParentId => $args{'ImmediateParentId'},
-                         Disabled => $args{'Disabled'},
-);
+    my $val = shift;
+    # if it's already disabled, we're good.
+    return (1) if ( $self->__Value('Disabled') == $val);
+    my $err = $self->_Set(Field => 'Disabled', Value => $val);
+    my ($retval, $msg) = $err->as_array();
+    unless ($retval) {
+        $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $self->Id .": $msg");
+        return ($err);
+    }
+    
+    my $member = $self->MemberObj();
+    if ( $member->IsGroup ) {
+        my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
+
+        $deletable->Limit( FIELD    => 'Via', OPERATOR => '=', VALUE    => $self->id );
+        $deletable->Limit( FIELD    => 'id', OPERATOR => '!=', VALUE    => $self->id );
+
+        while ( my $kid = $deletable->Next ) {
+            my $kid_err = $kid->SetDisabled($val );
+            unless ($kid_err) {
+                $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $kid->Id );
+                return ($kid_err);
+            }
+        }
+    }
+    return ($err);
+}
+
+
+
+=head2 GroupObj  
+
+Returns the RT::Principal object for this group Group
 
+=cut
+
+sub GroupObj {
+    my $self      = shift;
+    my $principal = RT::Principal->new( $self->CurrentUser );
+    $principal->Load( $self->GroupId );
+    return ($principal);
+}
+
+
+
+=head2 ImmediateParentObj  
+
+Returns the RT::Principal object for this group ImmediateParent
+
+=cut
+
+sub ImmediateParentObj {
+    my $self      = shift;
+    my $principal = RT::Principal->new( $self->CurrentUser );
+    $principal->Load( $self->ImmediateParentId );
+    return ($principal);
 }
 
 
 
-=item id
+=head2 MemberObj  
+
+Returns the RT::Principal object for this group member
+
+=cut
+
+sub MemberObj {
+    my $self      = shift;
+    my $principal = RT::Principal->new( $self->CurrentUser );
+    $principal->Load( $self->MemberId );
+    return ($principal);
+}
+
+# }}}
+
+
+
+
 
-Returns the current value of id. 
+
+=head2 id
+
+Returns the current value of id.
 (In the database, id is stored as int(11).)
 
 
 =cut
 
 
-=item GroupId
+=head2 GroupId
 
-Returns the current value of GroupId. 
+Returns the current value of GroupId.
 (In the database, GroupId is stored as int(11).)
 
 
 
-=item SetGroupId VALUE
+=head2 SetGroupId VALUE
 
 
-Set GroupId to VALUE. 
+Set GroupId to VALUE.
 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 (In the database, GroupId will be stored as a int(11).)
 
@@ -125,17 +341,17 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 =cut
 
 
-=item MemberId
+=head2 MemberId
 
-Returns the current value of MemberId. 
+Returns the current value of MemberId.
 (In the database, MemberId is stored as int(11).)
 
 
 
-=item SetMemberId VALUE
+=head2 SetMemberId VALUE
 
 
-Set MemberId to VALUE. 
+Set MemberId to VALUE.
 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 (In the database, MemberId will be stored as a int(11).)
 
@@ -143,17 +359,17 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 =cut
 
 
-=item Via
+=head2 Via
 
-Returns the current value of Via. 
+Returns the current value of Via.
 (In the database, Via is stored as int(11).)
 
 
 
-=item SetVia VALUE
+=head2 SetVia VALUE
 
 
-Set Via to VALUE. 
+Set Via to VALUE.
 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 (In the database, Via will be stored as a int(11).)
 
@@ -161,17 +377,17 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 =cut
 
 
-=item ImmediateParentId
+=head2 ImmediateParentId
 
-Returns the current value of ImmediateParentId. 
+Returns the current value of ImmediateParentId.
 (In the database, ImmediateParentId is stored as int(11).)
 
 
 
-=item SetImmediateParentId VALUE
+=head2 SetImmediateParentId VALUE
 
 
-Set ImmediateParentId to VALUE. 
+Set ImmediateParentId to VALUE.
 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 (In the database, ImmediateParentId will be stored as a int(11).)
 
@@ -179,17 +395,17 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 =cut
 
 
-=item Disabled
+=head2 Disabled
 
-Returns the current value of Disabled. 
+Returns the current value of Disabled.
 (In the database, Disabled is stored as smallint(6).)
 
 
 
-=item SetDisabled VALUE
+=head2 SetDisabled VALUE
 
 
-Set Disabled to VALUE. 
+Set Disabled to VALUE.
 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 (In the database, Disabled will be stored as a smallint(6).)
 
@@ -198,61 +414,68 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 
 
 
-sub _ClassAccessible {
+sub _CoreAccessible {
     {
-     
+
         id =>
-               {read => 1, type => 'int(11)', default => ''},
-        GroupId => 
-               {read => 1, write => 1, type => 'int(11)', default => ''},
-        MemberId => 
-               {read => 1, write => 1, type => 'int(11)', default => ''},
-        Via => 
-               {read => 1, write => 1, type => 'int(11)', default => ''},
-        ImmediateParentId => 
-               {read => 1, write => 1, type => 'int(11)', default => ''},
-        Disabled => 
-               {read => 1, write => 1, type => 'smallint(6)', default => '0'},
+                {read => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        GroupId =>
+                {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        MemberId =>
+                {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        Via =>
+                {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        ImmediateParentId =>
+                {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        Disabled =>
+                {read => 1, write => 1, sql_type => 5, length => 6,  is_blob => 0,  is_numeric => 1,  type => 'smallint(6)', default => '0'},
 
  }
 };
 
+sub Serialize {
+    die "CachedGroupMembers should never be serialized";
+}
 
-        eval "require RT::CachedGroupMember_Overlay";
-        if ($@ && $@ !~ qr{^Can't locate RT/CachedGroupMember_Overlay.pm}) {
-            die $@;
-        };
-
-        eval "require RT::CachedGroupMember_Vendor";
-        if ($@ && $@ !~ qr{^Can't locate RT/CachedGroupMember_Vendor.pm}) {
-            die $@;
-        };
-
-        eval "require RT::CachedGroupMember_Local";
-        if ($@ && $@ !~ qr{^Can't locate RT/CachedGroupMember_Local.pm}) {
-            die $@;
-        };
-
-
-
-
-=head1 SEE ALSO
-
-This class allows "overlay" methods to be placed
-into the following files _Overlay is for a System overlay by the original author,
-_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.  
-
-These overlay files can contain new subs or subs to replace existing subs in this module.
-
-If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line 
-
-   no warnings qw(redefine);
-
-so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
-
-RT::CachedGroupMember_Overlay, RT::CachedGroupMember_Vendor, RT::CachedGroupMember_Local
-
-=cut
+sub __DependsOn
+{
+    my $self = shift;
+    my %args = (
+        Shredder => undef,
+        Dependencies => undef,
+        @_,
+    );
+    my $deps = $args{'Dependencies'};
+    my $list = [];
+
+# deep memebership
+    my $objs = RT::CachedGroupMembers->new( $self->CurrentUser );
+    $objs->Limit( FIELD => 'Via', VALUE => $self->Id );
+    $objs->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->Id );
+    push( @$list, $objs );
+
+# principal lost group membership and lost some rights which he could delegate to
+# some body
+
+# XXX: Here is problem cause HasMemberRecursively would return true allways
+# cause we didn't delete anything yet. :(
+    # if pricipal is not member anymore(could be via other groups) then proceed
+    if( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
+        my $acl = RT::ACL->new( $self->CurrentUser );
+        $acl->LimitToPrincipal( Id => $self->GroupId );
+    }
+
+
+    $deps->_PushDependencies(
+        BaseObject => $self,
+        Flags => RT::Shredder::Constants::DEPENDS_ON,
+        TargetObjects => $list,
+        Shredder => $args{'Shredder'}
+    );
+
+    return $self->SUPER::__DependsOn( %args );
+}
 
+RT::Base->_ImportOverlays();
 
 1;