X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FACE.pm;h=20967365778963cf2c856de5463e5476dd03aca4;hb=HEAD;hp=dca50c359b52404f308b79963a58dd2ef556950c;hpb=75162bb14b3e38d66617077843f4dfdcaf09d5c4;p=freeside.git diff --git a/rt/lib/RT/ACE.pm b/rt/lib/RT/ACE.pm index dca50c359..209673657 100755 --- a/rt/lib/RT/ACE.pm +++ b/rt/lib/RT/ACE.pm @@ -2,7 +2,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) @@ -46,88 +46,598 @@ # # END BPS TAGGED BLOCK }}} -# Autogenerated by DBIx::SearchBuilder factory (by ) -# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST. -# -# !! DO NOT EDIT THIS FILE !! -# - -use strict; - +=head1 SYNOPSIS -=head1 NAME + use RT::ACE; + my $ace = RT::ACE->new($CurrentUser); -RT::ACE +=head1 DESCRIPTION -=head1 SYNOPSIS -=head1 DESCRIPTION =head1 METHODS + =cut + package RT::ACE; use base 'RT::Record'; -sub _Init { - my $self = shift; +sub Table {'ACL'} + + +use strict; +use warnings; + +require RT::Principals; +require RT::Queues; +require RT::Groups; + +our %RIGHTS; + +my (@_ACL_CACHE_HANDLERS); + - $self->Table('ACL'); - $self->SUPER::_Init(@_); -} +=head1 Rights +# Queue rights are the sort of queue rights that can only be granted +# to real people or groups +=cut + +=head2 LoadByValues PARAMHASH + +Load an ACE by specifying a paramhash with the following fields: + PrincipalId => undef, + PrincipalType => undef, + RightName => undef, -=head2 Create PARAMHASH + And either: -Create takes a hash of values and creates a row in the database: + Object => undef, - varchar(25) 'PrincipalType'. - int(11) 'PrincipalId'. - varchar(25) 'RightName'. - varchar(25) 'ObjectType'. - int(11) 'ObjectId'. - int(11) 'DelegatedBy'. - int(11) 'DelegatedFrom'. + OR + + ObjectType => undef, + ObjectId => undef =cut +sub LoadByValues { + my $self = shift; + my %args = ( PrincipalId => undef, + PrincipalType => undef, + RightName => undef, + Object => undef, + ObjectId => undef, + ObjectType => undef, + @_ ); + + if ( $args{'RightName'} ) { + my $canonic_name = $self->CanonicalizeRightName( $args{'RightName'} ); + unless ( $canonic_name ) { + return wantarray ? ( 0, $self->loc("Invalid right. Couldn't canonicalize right '[_1]'", $args{'RightName'}) ) : 0; + } + $args{'RightName'} = $canonic_name; + } + + my $princ_obj; + ( $princ_obj, $args{'PrincipalType'} ) = + $self->_CanonicalizePrincipal( $args{'PrincipalId'}, + $args{'PrincipalType'} ); + + unless ( $princ_obj->id ) { + return wantarray ? ( 0, + $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} ) + ) : 0; + } + + my ($object, $object_type, $object_id) = $self->_ParseObjectArg( %args ); + unless( $object ) { + return wantarray ? ( 0, $self->loc("System error. Right not granted.")) : 0; + } + + $self->LoadByCols( PrincipalId => $princ_obj->Id, + PrincipalType => $args{'PrincipalType'}, + RightName => $args{'RightName'}, + ObjectType => $object_type, + ObjectId => $object_id); + + #If we couldn't load it. + unless ( $self->Id ) { + return wantarray ? ( 0, $self->loc("ACE not found") ) : 0; + } + + # if we could + return wantarray ? ( $self->Id, $self->loc("Right Loaded") ) : $self->Id; +} + +=head2 Create + +PARAMS is a parameter hash with the following elements: + + PrincipalId => The id of an RT::Principal object + PrincipalType => "User" "Group" or any Role type + RightName => the name of a right. in any case + + + Either: + + Object => An object to create rights for. ususally, an RT::Queue or RT::Group + This should always be a DBIx::SearchBuilder::Record subclass + + OR + + ObjectType => the type of the object in question (ref ($object)) + ObjectId => the id of the object in question $object->Id + + + + Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's false. + + + +=cut + sub Create { my $self = shift; - my %args = ( - PrincipalType => '', - PrincipalId => '0', - RightName => '', - ObjectType => '', - ObjectId => '0', - DelegatedBy => '0', - DelegatedFrom => '0', - - @_); - $self->SUPER::Create( - PrincipalType => $args{'PrincipalType'}, - PrincipalId => $args{'PrincipalId'}, - RightName => $args{'RightName'}, - ObjectType => $args{'ObjectType'}, - ObjectId => $args{'ObjectId'}, - DelegatedBy => $args{'DelegatedBy'}, - DelegatedFrom => $args{'DelegatedFrom'}, -); + my %args = ( + PrincipalId => undef, + PrincipalType => undef, + RightName => undef, + Object => undef, + @_ + ); + + unless ( $args{'RightName'} ) { + return ( 0, $self->loc('No right specified') ); + } + + #if we haven't specified any sort of right, we're talking about a global right + if (!defined $args{'Object'} && !defined $args{'ObjectId'} && !defined $args{'ObjectType'}) { + $args{'Object'} = $RT::System; + } + ($args{'Object'}, $args{'ObjectType'}, $args{'ObjectId'}) = $self->_ParseObjectArg( %args ); + unless( $args{'Object'} ) { + return ( 0, $self->loc("System error. Right not granted.") ); + } + + # Validate the principal + my $princ_obj; + ( $princ_obj, $args{'PrincipalType'} ) = + $self->_CanonicalizePrincipal( $args{'PrincipalId'}, + $args{'PrincipalType'} ); + + unless ( $princ_obj->id ) { + return ( 0, + $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} ) + ); + } + + # }}} + + # Check the ACL + + if (ref( $args{'Object'}) eq 'RT::Group' ) { + unless ( $self->CurrentUser->HasRight( Object => $args{'Object'}, + Right => 'AdminGroup' ) + ) { + return ( 0, $self->loc('Permission Denied') ); + } + } + + else { + unless ( $self->CurrentUser->HasRight( Object => $args{'Object'}, Right => 'ModifyACL' )) { + return ( 0, $self->loc('Permission Denied') ); + } + } + # }}} + + # Canonicalize and check the right name + my $canonic_name = $self->CanonicalizeRightName( $args{'RightName'} ); + unless ( $canonic_name ) { + return ( 0, $self->loc("Invalid right. Couldn't canonicalize right '[_1]'", $args{'RightName'}) ); + } + $args{'RightName'} = $canonic_name; + + #check if it's a valid RightName + if ( $args{'Object'}->can('AvailableRights') ) { + my $available = $args{'Object'}->AvailableRights($princ_obj); + unless ( grep $_ eq $args{'RightName'}, map $self->CanonicalizeRightName( $_ ), keys %$available ) { + $RT::Logger->warning( + "Couldn't validate right name '$args{'RightName'}'" + ." for object of ". ref( $args{'Object'} ) ." class" + ); + return ( 0, $self->loc('Invalid right') ); + } + } + # }}} + + # Make sure the right doesn't already exist. + $self->LoadByCols( PrincipalId => $princ_obj->id, + PrincipalType => $args{'PrincipalType'}, + RightName => $args{'RightName'}, + ObjectType => $args{'ObjectType'}, + ObjectId => $args{'ObjectId'}, + ); + if ( $self->Id ) { + return ( 0, $self->loc('[_1] already has that right', + $princ_obj->Object->Name) ); + } + + my $id = $self->SUPER::Create( PrincipalId => $princ_obj->id, + PrincipalType => $args{'PrincipalType'}, + RightName => $args{'RightName'}, + ObjectType => ref( $args{'Object'} ), + ObjectId => $args{'Object'}->id, + ); + + if ( $id ) { + RT::ACE->InvalidateCaches( + Action => "Grant", + RightName => $self->RightName, + ACE => $self, + ); + return ( $id, $self->loc('Right Granted') ); + } + else { + return ( 0, $self->loc('System error. Right not granted.') ); + } +} + + + +=head2 Delete { InsideTransaction => undef} + +Delete this object. This method should ONLY ever be called from RT::User or RT::Group (or from itself) +If this is being called from within a transaction, specify a true value for the parameter InsideTransaction. +Really, DBIx::SearchBuilder should use and/or fake subtransactions + +This routine will also recurse and delete any delegations of this right + +=cut + +sub Delete { + my $self = shift; + + unless ( $self->Id ) { + return ( 0, $self->loc('Right not loaded.') ); + } + + # A user can delete an ACE if the current user has the right to modify it and it's not a delegated ACE + # or if it's a delegated ACE and it was delegated by the current user + unless ($self->CurrentUser->HasRight(Right => 'ModifyACL', Object => $self->Object)) { + return ( 0, $self->loc('Permission Denied') ); + } + $self->_Delete(@_); +} + +# Helper for Delete with no ACL check +sub _Delete { + my $self = shift; + my %args = ( InsideTransaction => undef, + @_ ); + + my $InsideTransaction = $args{'InsideTransaction'}; + + $RT::Handle->BeginTransaction() unless $InsideTransaction; + + my $right = $self->RightName; + + my ( $val, $msg ) = $self->SUPER::Delete(@_); + + if ($val) { + RT::ACE->InvalidateCaches( Action => "Revoke", RightName => $right ); + $RT::Handle->Commit() unless $InsideTransaction; + return ( $val, $self->loc('Right revoked') ); + } + + $RT::Handle->Rollback() unless $InsideTransaction; + return ( 0, $self->loc('Right could not be revoked') ); +} + + + +=head2 _BootstrapCreate + +Grant a right with no error checking and no ACL. this is _only_ for +installation. If you use this routine without the author's explicit +written approval, he will hunt you down and make you spend eternity +translating mozilla's code into FORTRAN or intercal. + +If you think you need this routine, you've mistaken. + +=cut + +sub _BootstrapCreate { + my $self = shift; + my %args = (@_); + + # When bootstrapping, make sure we get the _right_ users + if ( $args{'UserId'} ) { + my $user = RT::User->new( $self->CurrentUser ); + $user->Load( $args{'UserId'} ); + delete $args{'UserId'}; + $args{'PrincipalId'} = $user->PrincipalId; + $args{'PrincipalType'} = 'User'; + } + + my $id = $self->SUPER::Create(%args); + + if ( $id > 0 ) { + return ($id); + } + else { + $RT::Logger->err('System error. right not granted.'); + return (undef); + } + +} + +=head2 InvalidateCaches + +Calls any registered ACL cache handlers (see L). + +Usually called from L and L. + +=cut + +sub InvalidateCaches { + my $class = shift; + + for my $handler (@_ACL_CACHE_HANDLERS) { + next unless ref($handler) eq "CODE"; + $handler->(@_); + } +} + +=head2 RegisterCacheHandler + +Class method. Takes a coderef and adds it to the ACL cache handlers. These +handlers are called by L, usually called itself from +L and L. + +The handlers are passed a hash which may contain any (or none) of these +optional keys: + +=over + +=item Action + +A string indicating the action that (may have) invalidated the cache. Expected +values are currently: + +=over + +=item Grant + +=item Revoke + +=back + +However, other values may be passed in the future. + +=item RightName + +The (canonicalized) right being granted or revoked. + +=item ACE + +The L object just created. + +=back + +Your handler should be flexible enough to account for additional arguments +being passed in the future. + +=cut + +sub RegisterCacheHandler { + push @_ACL_CACHE_HANDLERS, $_[1]; +} + +sub RightName { + my $self = shift; + my $val = $self->_Value('RightName'); + return $val unless $val; + + my $available = $self->Object->AvailableRights; + foreach my $right ( keys %$available ) { + return $right if $val eq $self->CanonicalizeRightName($right); + } + $RT::Logger->error("Invalid right. Couldn't canonicalize right '$val'"); + return $val; } +=head2 CanonicalizeRightName + +Takes a queue or system right name in any case and returns it in +the correct case. If it's not found, will return undef. + +=cut + +sub CanonicalizeRightName { + my $self = shift; + my $name = shift; + for my $class (sort keys %RIGHTS) { + return $RIGHTS{$class}{ lc $name }{Name} + if $RIGHTS{$class}{ lc $name }; + } + return undef; +} + + + +=head2 Object + +If the object this ACE applies to is a queue, returns the queue object. +If the object this ACE applies to is a group, returns the group object. +If it's the system object, returns undef. + +If the user has no rights, returns undef. + +=cut + + + + +sub Object { + my $self = shift; + + my $appliesto_obj; + + if ($self->__Value('ObjectType') && $self->__Value('ObjectType')->DOES('RT::Record::Role::Rights') ) { + $appliesto_obj = $self->__Value('ObjectType')->new($self->CurrentUser); + unless (ref( $appliesto_obj) eq $self->__Value('ObjectType')) { + return undef; + } + $appliesto_obj->Load( $self->__Value('ObjectId') ); + return ($appliesto_obj); + } + else { + $RT::Logger->warning( "$self -> Object called for an object " + . "of an unknown type:" + . $self->__Value('ObjectType') ); + return (undef); + } +} + + + +=head2 PrincipalObj + +Returns the RT::Principal object for this ACE. + +=cut + +sub PrincipalObj { + my $self = shift; + + my $princ_obj = RT::Principal->new( $self->CurrentUser ); + $princ_obj->Load( $self->__Value('PrincipalId') ); + + unless ( $princ_obj->Id ) { + $RT::Logger->err( + "ACE " . $self->Id . " couldn't load its principal object" ); + } + return ($princ_obj); + +} + + + + +sub _Set { + my $self = shift; + return ( 0, $self->loc("ACEs can only be created and deleted.") ); +} + + + +sub _Value { + my $self = shift; + + if ( $self->PrincipalObj->IsGroup + && $self->PrincipalObj->Object->HasMemberRecursively( + $self->CurrentUser->PrincipalObj + ) + ) { + return ( $self->__Value(@_) ); + } + elsif ( $self->CurrentUser->HasRight(Right => 'ShowACL', Object => $self->Object) ) { + return ( $self->__Value(@_) ); + } + else { + return undef; + } +} + + + + + +=head2 _CanonicalizePrincipal (PrincipalId, PrincipalType) + +Takes a principal id and a principal type. + +If the principal is a user, resolves it to the proper acl equivalence group. +Returns a tuple of (RT::Principal, PrincipalType) for the principal we really want to work with + +=cut + +sub _CanonicalizePrincipal { + my $self = shift; + my $princ_id = shift; + my $princ_type = shift || ''; + + my $princ_obj = RT::Principal->new(RT->SystemUser); + $princ_obj->Load($princ_id); + + unless ( $princ_obj->Id ) { + use Carp; + $RT::Logger->crit(Carp::longmess); + $RT::Logger->crit("Can't load a principal for id $princ_id"); + return ( $princ_obj, undef ); + } + + # Rights never get granted to users. they get granted to their + # ACL equivalence groups + if ( $princ_type eq 'User' ) { + my $equiv_group = RT::Group->new( $self->CurrentUser ); + $equiv_group->LoadACLEquivalenceGroup($princ_obj); + unless ( $equiv_group->Id ) { + $RT::Logger->crit( "No ACL equiv group for princ " . $princ_obj->id ); + return ( RT::Principal->new(RT->SystemUser), undef ); + } + $princ_obj = $equiv_group->PrincipalObj(); + $princ_type = 'Group'; + + } + return ( $princ_obj, $princ_type ); +} + +sub _ParseObjectArg { + my $self = shift; + my %args = ( Object => undef, + ObjectId => undef, + ObjectType => undef, + @_ ); + + if( $args{'Object'} && ($args{'ObjectId'} || $args{'ObjectType'}) ) { + $RT::Logger->crit( "Method called with an ObjectType or an ObjectId and Object args" ); + return (); + } elsif( $args{'Object'} && ref($args{'Object'}) && !$args{'Object'}->can('id') ) { + $RT::Logger->crit( "Method called called Object that has no id method" ); + return (); + } elsif( $args{'Object'} ) { + my $obj = $args{'Object'}; + return ($obj, ref $obj, $obj->id); + } elsif ( $args{'ObjectType'} ) { + my $obj = $args{'ObjectType'}->new( $self->CurrentUser ); + $obj->Load( $args{'ObjectId'} ); + return ($obj, ref $obj, $obj->id); + } else { + $RT::Logger->crit( "Method called with wrong args" ); + return (); + } +} + + +# }}} + =head2 id -Returns the current value of id. +Returns the current value of id. (In the database, id is stored as int(11).) @@ -136,7 +646,7 @@ Returns the current value of id. =head2 PrincipalType -Returns the current value of PrincipalType. +Returns the current value of PrincipalType. (In the database, PrincipalType is stored as varchar(25).) @@ -144,7 +654,7 @@ Returns the current value of PrincipalType. =head2 SetPrincipalType VALUE -Set PrincipalType to VALUE. +Set PrincipalType to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, PrincipalType will be stored as a varchar(25).) @@ -154,7 +664,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure. =head2 PrincipalId -Returns the current value of PrincipalId. +Returns the current value of PrincipalId. (In the database, PrincipalId is stored as int(11).) @@ -162,7 +672,7 @@ Returns the current value of PrincipalId. =head2 SetPrincipalId VALUE -Set PrincipalId to VALUE. +Set PrincipalId to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, PrincipalId will be stored as a int(11).) @@ -172,7 +682,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure. =head2 RightName -Returns the current value of RightName. +Returns the current value of RightName. (In the database, RightName is stored as varchar(25).) @@ -180,7 +690,7 @@ Returns the current value of RightName. =head2 SetRightName VALUE -Set RightName to VALUE. +Set RightName to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, RightName will be stored as a varchar(25).) @@ -190,7 +700,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure. =head2 ObjectType -Returns the current value of ObjectType. +Returns the current value of ObjectType. (In the database, ObjectType is stored as varchar(25).) @@ -198,7 +708,7 @@ Returns the current value of ObjectType. =head2 SetObjectType VALUE -Set ObjectType to VALUE. +Set ObjectType to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, ObjectType will be stored as a varchar(25).) @@ -208,7 +718,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure. =head2 ObjectId -Returns the current value of ObjectId. +Returns the current value of ObjectId. (In the database, ObjectId is stored as int(11).) @@ -216,7 +726,7 @@ Returns the current value of ObjectId. =head2 SetObjectId VALUE -Set ObjectId to VALUE. +Set ObjectId to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, ObjectId will be stored as a int(11).) @@ -224,38 +734,34 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure. =cut -=head2 DelegatedBy +=head2 Creator -Returns the current value of DelegatedBy. -(In the database, DelegatedBy is stored as int(11).) +Returns the current value of Creator. +(In the database, Creator is stored as int(11).) +=cut -=head2 SetDelegatedBy VALUE - - -Set DelegatedBy to VALUE. -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, DelegatedBy will be stored as a int(11).) +=head2 Created +Returns the current value of Created. +(In the database, Created is stored as datetime.) =cut -=head2 DelegatedFrom - -Returns the current value of DelegatedFrom. -(In the database, DelegatedFrom is stored as int(11).) - +=head2 LastUpdatedBy +Returns the current value of LastUpdatedBy. +(In the database, LastUpdatedBy is stored as int(11).) -=head2 SetDelegatedFrom VALUE +=cut -Set DelegatedFrom to VALUE. -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, DelegatedFrom will be stored as a int(11).) +=head2 LastUpdated +Returns the current value of LastUpdated. +(In the database, LastUpdated is stored as datetime.) =cut @@ -263,46 +769,41 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure. sub _CoreAccessible { { - + id => - {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, - PrincipalType => - {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''}, - PrincipalId => - {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, - RightName => - {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''}, - ObjectType => - {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''}, - ObjectId => - {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, - DelegatedBy => - {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, - DelegatedFrom => - {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, + {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, + PrincipalType => + {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''}, + PrincipalId => + {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, + RightName => + {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''}, + ObjectType => + {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''}, + ObjectId => + {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, + Creator => + {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, + Created => + {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, + LastUpdatedBy => + {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, + LastUpdated => + {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, } }; -RT::Base->_ImportOverlays(); - -=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. - -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. +sub FindDependencies { + my $self = shift; + my ($walker, $deps) = @_; -RT::ACE_Overlay, RT::ACE_Vendor, RT::ACE_Local + $self->SUPER::FindDependencies($walker, $deps); -=cut + $deps->Add( out => $self->PrincipalObj->Object ); + $deps->Add( out => $self->Object ); +} +RT::Base->_ImportOverlays(); 1;