X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=rt%2Flib%2FRT%2FSystem.pm;h=2c6b58d1e8baee0cc68a01c72776ad0083007066;hp=2a23e32e4d65911b1b1800e5727e6bd52c99fb3d;hb=187086c479a09629b7d180eec513fb7657f4e291;hpb=63a268637b2d51a8766412617724b9436439deb6 diff --git a/rt/lib/RT/System.pm b/rt/lib/RT/System.pm index 2a23e32e4..2c6b58d1e 100644 --- a/rt/lib/RT/System.pm +++ b/rt/lib/RT/System.pm @@ -1,40 +1,40 @@ # BEGIN BPS TAGGED BLOCK {{{ -# +# # COPYRIGHT: -# -# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC -# -# +# +# This software is Copyright (c) 1996-2018 Best Practical Solutions, LLC +# +# # (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. -# +# # 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 @@ -43,7 +43,7 @@ # 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 }}} =head1 NAME @@ -69,82 +69,87 @@ package RT::System; use strict; use warnings; -use base qw/RT::Record/; -use RT::ACL; +use base qw/RT::Record/; -# System rights are rights granted to the whole system -# XXX TODO Can't localize these outside of having an object around. -our $RIGHTS = { - SuperUser => 'Do anything and everything', # loc_pair - AdminAllPersonalGroups => - "Create, delete and modify the members of any user's personal groups", # loc_pair - AdminOwnPersonalGroups => - 'Create, delete and modify the members of personal groups', # loc_pair - AdminUsers => 'Create, delete and modify users', # loc_pair - ModifySelf => "Modify one's own RT account", # loc_pair - DelegateRights => - "Delegate specific rights which have been granted to you.", # loc_pair - ShowConfigTab => "show Configuration tab", # loc_pair - ShowApprovalsTab => "show Approvals tab", # loc_pair - LoadSavedSearch => "allow loading of saved searches", # loc_pair - CreateSavedSearch => "allow creation of saved searches", # loc_pair -}; - -# Tell RT::ACE that this sort of object can get acls granted -$RT::ACE::OBJECT_TYPES{'RT::System'} = 1; - -foreach my $right ( keys %{$RIGHTS} ) { - $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right; -} +use Role::Basic 'with'; +with "RT::Record::Role::Roles", + "RT::Record::Role::Rights" => { -excludes => [qw/AvailableRights RightCategories/] }; +use RT::ACL; +use RT::ACE; +use Data::GUID; + +__PACKAGE__->AddRight( Admin => SuperUser => 'Do anything and everything'); # loc +__PACKAGE__->AddRight( Staff => ShowUserHistory => 'Show history of public user properties'); # loc +__PACKAGE__->AddRight( Admin => AdminUsers => 'Create, modify and delete users'); # loc +__PACKAGE__->AddRight( Staff => ModifySelf => "Modify one's own RT account"); # loc +__PACKAGE__->AddRight( Staff => ShowArticlesMenu => 'Show Articles menu'); # loc +__PACKAGE__->AddRight( Admin => ShowConfigTab => 'Show Admin menu'); # loc +__PACKAGE__->AddRight( Admin => ShowApprovalsTab => 'Show Approvals tab'); # loc +__PACKAGE__->AddRight( Staff => ShowGlobalTemplates => 'Show global templates'); # loc +__PACKAGE__->AddRight( General => LoadSavedSearch => 'Allow loading of saved searches'); # loc +__PACKAGE__->AddRight( General => CreateSavedSearch => 'Allow creation of saved searches'); # loc +__PACKAGE__->AddRight( Admin => ExecuteCode => 'Allow writing Perl code in templates, scrips, etc'); # loc + +#freeside +__PACKAGE__->AddRight( Staff => BulkUpdateTickets => 'Bulk update tickets'); =head2 AvailableRights -Returns a hash of available rights for this object. -The keys are the right names and the values are a -description of what the rights do. +Returns a hashref of available rights for this object. The keys are the +right names and the values are a description of what the rights do. -This method as well returns rights of other RT objects, -like L or L. To allow users to apply -those rights globally. +This method as well returns rights of other RT objects, like +L or L, to allow users to apply those rights +globally. -=cut +If an L is passed as the first argument, the available +rights will be limited to ones which make sense for the principal. +Currently only role groups are supported and rights announced by object +types to which the role group doesn't apply are not returned. +=cut -use RT::CustomField; -use RT::Queue; -use RT::Group; sub AvailableRights { my $self = shift; - - my $queue = RT::Queue->new($RT::SystemUser); - my $group = RT::Group->new($RT::SystemUser); - my $cf = RT::CustomField->new($RT::SystemUser); - - my $qr = $queue->AvailableRights(); - my $gr = $group->AvailableRights(); - my $cr = $cf->AvailableRights(); - - # Build a merged list of all system wide rights, queue rights and group rights. - my %rights = (%{$RIGHTS}, %{$gr}, %{$qr}, %{$cr}); - - return(\%rights); + my $principal = shift; + my $class = ref($self) || $self; + + my @rights; + if ($principal and $principal->IsRoleGroup) { + my $role = $principal->Object->Name; + for my $class (keys %RT::ACE::RIGHTS) { + next unless $class->DOES('RT::Record::Role::Roles') and $class->HasRole($role) and $class ne "RT::System"; + push @rights, values %{ $RT::ACE::RIGHTS{$class} }; + } + } else { + @rights = map {values %{$_}} values %RT::ACE::RIGHTS; + } + + my %rights; + $rights{$_->{Name}} = $_->{Description} for @rights; + + delete $rights{ExecuteCode} if RT->Config->Get('DisallowExecuteCode'); + + return \%rights; } -=head2 AddRights C, C [, ...] +=head2 RightCategories -Adds the given rights to the list of possible rights. This method -should be called during server startup, not at runtime. +Returns a hashref where the keys are rights for this type of object and the +values are the category (General, Staff, Admin) the right falls into. =cut -sub AddRights { - my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__; - my %new = @_; - $RIGHTS = { %$RIGHTS, %new }; - %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES, - map { lc($_) => $_ } keys %new); +sub RightCategories { + my $self = shift; + my $class = ref($self) || $self; + + my %rights; + $rights{$_->{Name}} = $_->{Category} + for map {values %{$_}} values %RT::ACE::RIGHTS; + return \%rights; } sub _Init { @@ -161,6 +166,8 @@ Returns RT::System's id. It's 1. *Id = \&id; sub id { return 1 } +sub UID { return "RT::System" } + =head2 Load Since this object is pretending to be an RT::Record, we need a load method. @@ -179,19 +186,154 @@ sub SubjectTag { my $self = shift; my $queue = shift; - my $map = $self->FirstAttribute('BrandedSubjectTag'); - $map = $map->Content if $map; - return $queue ? undef : () unless $map; + use Carp; + confess "SubjectTag called on $self with $queue" if $queue; - return $map->{ $queue->id } if $queue; + return $queue->SubjectTag if $queue; - my %seen = (); - return grep !$seen{lc $_}++, values %$map; + my $queues = RT::Queues->new( $self->CurrentUser ); + $queues->Limit( FIELD => 'SubjectTag', OPERATOR => 'IS NOT', VALUE => 'NULL' ); + return $queues->DistinctFieldValues('SubjectTag'); } -eval "require RT::System_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/System_Vendor.pm}); -eval "require RT::System_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/System_Local.pm}); +=head2 QueueCacheNeedsUpdate ( 1 ) + +Attribute to decide when SelectQueue needs to flush the list of queues +and retrieve new ones. Set when queues are created, enabled/disabled +and on certain acl changes. Should also better understand group management. + +If passed a true value, will update the attribute to be the current time. + +=cut + +sub QueueCacheNeedsUpdate { + my $self = shift; + my $update = shift; + + if ($update) { + return $self->SetAttribute(Name => 'QueueCacheNeedsUpdate', Content => time); + } else { + my $cache = $self->FirstAttribute('QueueCacheNeedsUpdate'); + return (defined $cache ? $cache->Content : 0 ); + } +} + +=head2 AddUpgradeHistory package, data + +Adds an entry to the upgrade history database. The package can be either C +for core RT upgrades, or the fully qualified name of a plugin. The data must be +a hash reference. + +=cut + +sub AddUpgradeHistory { + my $self = shift; + my $package = shift; + my $data = shift; + + $data->{timestamp} ||= time; + $data->{rt_version} ||= $RT::VERSION; + + my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory'); + my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {}; + + push @{ $upgrade_history->{$package} }, $data; + + $self->SetAttribute( + Name => 'UpgradeHistory', + Content => $upgrade_history, + ); +} + +=head2 UpgradeHistory [package] + +Returns the entries of RT's upgrade history. If a package is specified, the list +of upgrades for that package will be returned. Otherwise a hash reference of +C<< package => [upgrades] >> will be returned. + +=cut + +sub UpgradeHistory { + my $self = shift; + my $package = shift; + + my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory'); + my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {}; + + if ($package) { + return @{ $upgrade_history->{$package} || [] }; + } + + return $upgrade_history; +} + +sub ParsedUpgradeHistory { + my $self = shift; + my $package = shift; + + my $version_status = "Current version: "; + if ( $package eq 'RT' ){ + $version_status .= $RT::VERSION; + } elsif ( grep {/$package/} @{RT->Config->Get('Plugins')} ) { + no strict 'refs'; + $version_status .= ${ $package . '::VERSION' }; + } else { + $version_status = "Not currently loaded"; + } + + my %ids; + my @lines; + + my @events = $self->UpgradeHistory( $package ); + for my $event (@events) { + if ($event->{stage} eq 'before' or (($event->{action}||'') eq 'insert' and not $event->{full_id})) { + if (not $event->{full_id}) { + # For upgrade done in the 4.1 series without GUIDs + if (($event->{type}||'') eq 'full upgrade') { + $event->{full_id} = $event->{individual_id} = Data::GUID->new->as_string; + } else { + $event->{individual_id} = Data::GUID->new->as_string; + $event->{full_id} = (@lines ? $lines[-1]{full_id} : Data::GUID->new->as_string); + } + $event->{return_value} = [1] if $event->{stage} eq 'after'; + } + if ($ids{$event->{full_id}}) { + my $kids = $ids{$event->{full_id}}{sub_events} ||= []; + # Stitch non-"upgrade"s beneath the previous "upgrade" + if ( @{$kids} and $event->{action} ne 'upgrade' and $kids->[-1]{action} eq 'upgrade') { + push @{ $kids->[-1]{sub_events} }, $event; + } else { + push @{ $kids }, $event; + } + } else { + push @lines, $event; + } + $ids{$event->{individual_id}} = $event; + } elsif ($event->{stage} eq 'after') { + if (not $event->{individual_id}) { + if (($event->{type}||'') eq 'full upgrade') { + $lines[-1]{end} = $event->{timestamp} if @lines; + } elsif (($event->{type}||'') eq 'individual upgrade') { + $lines[-1]{sub_events}[-1]{end} = $event->{timestamp} + if @lines and @{ $lines[-1]{sub_events} }; + } + } elsif ($ids{$event->{individual_id}}) { + my $end = $event; + $event = $ids{$event->{individual_id}}; + $event->{end} = $end->{timestamp}; + + $end->{return_value} = [ split ', ', $end->{return_value}, 2 ] + if $end->{return_value} and not ref $end->{return_value}; + $event->{return_value} = $end->{return_value}; + $event->{content} ||= $end->{content}; + } + } + } + + return ($version_status, @lines); +} + + +RT::Base->_ImportOverlays(); 1;