1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
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
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.
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.
30 # CONTRIBUTION SUBMISSION POLICY:
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.)
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.
47 # END BPS TAGGED BLOCK }}}
55 RT::System is a simple global object used as a focal point for things
58 It works sort of like an RT::Record, except it's really a single object that has
59 an id of "1" when instantiated.
61 This gets used by the ACL system so that you can have rights for the scope "RT::System"
63 In the future, there will probably be other API goodness encapsulated here.
73 use base qw/RT::Record/;
75 use Role::Basic 'with';
76 with "RT::Record::Role::Roles",
77 "RT::Record::Role::Rights" => { -excludes => [qw/AvailableRights RightCategories/] };
83 __PACKAGE__->AddRight( Admin => SuperUser => 'Do anything and everything'); # loc
84 __PACKAGE__->AddRight( Staff => ShowUserHistory => 'Show history of public user properties'); # loc
85 __PACKAGE__->AddRight( Admin => AdminUsers => 'Create, modify and delete users'); # loc
86 __PACKAGE__->AddRight( Staff => ModifySelf => "Modify one's own RT account"); # loc
87 __PACKAGE__->AddRight( Staff => ShowArticlesMenu => 'Show Articles menu'); # loc
88 __PACKAGE__->AddRight( Admin => ShowConfigTab => 'Show Admin menu'); # loc
89 __PACKAGE__->AddRight( Admin => ShowApprovalsTab => 'Show Approvals tab'); # loc
90 __PACKAGE__->AddRight( Staff => ShowGlobalTemplates => 'Show global templates'); # loc
91 __PACKAGE__->AddRight( General => LoadSavedSearch => 'Allow loading of saved searches'); # loc
92 __PACKAGE__->AddRight( General => CreateSavedSearch => 'Allow creation of saved searches'); # loc
93 __PACKAGE__->AddRight( Admin => ExecuteCode => 'Allow writing Perl code in templates, scrips, etc'); # loc
96 __PACKAGE__->AddRight( Staff => BulkUpdateTickets => 'Bulk update tickets');
98 =head2 AvailableRights
100 Returns a hashref of available rights for this object. The keys are the
101 right names and the values are a description of what the rights do.
103 This method as well returns rights of other RT objects, like
104 L<RT::Queue> or L<RT::Group>, to allow users to apply those rights
107 If an L<RT::Principal> is passed as the first argument, the available
108 rights will be limited to ones which make sense for the principal.
109 Currently only role groups are supported and rights announced by object
110 types to which the role group doesn't apply are not returned.
114 sub AvailableRights {
116 my $principal = shift;
117 my $class = ref($self) || $self;
120 if ($principal and $principal->IsRoleGroup) {
121 my $role = $principal->Object->Name;
122 for my $class (keys %RT::ACE::RIGHTS) {
123 next unless $class->DOES('RT::Record::Role::Roles') and $class->HasRole($role) and $class ne "RT::System";
124 push @rights, values %{ $RT::ACE::RIGHTS{$class} };
127 @rights = map {values %{$_}} values %RT::ACE::RIGHTS;
131 $rights{$_->{Name}} = $_->{Description} for @rights;
133 delete $rights{ExecuteCode} if RT->Config->Get('DisallowExecuteCode');
138 =head2 RightCategories
140 Returns a hashref where the keys are rights for this type of object and the
141 values are the category (General, Staff, Admin) the right falls into.
145 sub RightCategories {
147 my $class = ref($self) || $self;
150 $rights{$_->{Name}} = $_->{Category}
151 for map {values %{$_}} values %RT::ACE::RIGHTS;
157 $self->SUPER::_Init (@_) if @_ && $_[0];
162 Returns RT::System's id. It's 1.
169 sub UID { return "RT::System" }
173 Since this object is pretending to be an RT::Record, we need a load method.
178 sub Load { return 1 }
179 sub Name { return 'RT System' }
180 sub __Set { return 0 }
181 sub __Value { return 0 }
182 sub Create { return 0 }
183 sub Delete { return 0 }
190 confess "SubjectTag called on $self with $queue" if $queue;
192 return $queue->SubjectTag if $queue;
194 my $queues = RT::Queues->new( $self->CurrentUser );
195 $queues->Limit( FIELD => 'SubjectTag', OPERATOR => 'IS NOT', VALUE => 'NULL' );
196 return $queues->DistinctFieldValues('SubjectTag');
199 =head2 QueueCacheNeedsUpdate ( 1 )
201 Attribute to decide when SelectQueue needs to flush the list of queues
202 and retrieve new ones. Set when queues are created, enabled/disabled
203 and on certain acl changes. Should also better understand group management.
205 If passed a true value, will update the attribute to be the current time.
209 sub QueueCacheNeedsUpdate {
214 return $self->SetAttribute(Name => 'QueueCacheNeedsUpdate', Content => time);
216 my $cache = $self->FirstAttribute('QueueCacheNeedsUpdate');
217 return (defined $cache ? $cache->Content : 0 );
221 =head2 AddUpgradeHistory package, data
223 Adds an entry to the upgrade history database. The package can be either C<RT>
224 for core RT upgrades, or the fully qualified name of a plugin. The data must be
229 sub AddUpgradeHistory {
234 $data->{timestamp} ||= time;
235 $data->{rt_version} ||= $RT::VERSION;
237 my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory');
238 my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {};
240 push @{ $upgrade_history->{$package} }, $data;
243 Name => 'UpgradeHistory',
244 Content => $upgrade_history,
248 =head2 UpgradeHistory [package]
250 Returns the entries of RT's upgrade history. If a package is specified, the list
251 of upgrades for that package will be returned. Otherwise a hash reference of
252 C<< package => [upgrades] >> will be returned.
260 my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory');
261 my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {};
264 return @{ $upgrade_history->{$package} || [] };
267 return $upgrade_history;
270 sub ParsedUpgradeHistory {
274 my $version_status = "Current version: ";
275 if ( $package eq 'RT' ){
276 $version_status .= $RT::VERSION;
277 } elsif ( grep {/$package/} @{RT->Config->Get('Plugins')} ) {
279 $version_status .= ${ $package . '::VERSION' };
281 $version_status = "Not currently loaded";
287 my @events = $self->UpgradeHistory( $package );
288 for my $event (@events) {
289 if ($event->{stage} eq 'before' or (($event->{action}||'') eq 'insert' and not $event->{full_id})) {
290 if (not $event->{full_id}) {
291 # For upgrade done in the 4.1 series without GUIDs
292 if (($event->{type}||'') eq 'full upgrade') {
293 $event->{full_id} = $event->{individual_id} = Data::GUID->new->as_string;
295 $event->{individual_id} = Data::GUID->new->as_string;
296 $event->{full_id} = (@lines ? $lines[-1]{full_id} : Data::GUID->new->as_string);
298 $event->{return_value} = [1] if $event->{stage} eq 'after';
300 if ($ids{$event->{full_id}}) {
301 my $kids = $ids{$event->{full_id}}{sub_events} ||= [];
302 # Stitch non-"upgrade"s beneath the previous "upgrade"
303 if ( @{$kids} and $event->{action} ne 'upgrade' and $kids->[-1]{action} eq 'upgrade') {
304 push @{ $kids->[-1]{sub_events} }, $event;
306 push @{ $kids }, $event;
311 $ids{$event->{individual_id}} = $event;
312 } elsif ($event->{stage} eq 'after') {
313 if (not $event->{individual_id}) {
314 if (($event->{type}||'') eq 'full upgrade') {
315 $lines[-1]{end} = $event->{timestamp} if @lines;
316 } elsif (($event->{type}||'') eq 'individual upgrade') {
317 $lines[-1]{sub_events}[-1]{end} = $event->{timestamp}
318 if @lines and @{ $lines[-1]{sub_events} };
320 } elsif ($ids{$event->{individual_id}}) {
322 $event = $ids{$event->{individual_id}};
323 $event->{end} = $end->{timestamp};
325 $end->{return_value} = [ split ', ', $end->{return_value}, 2 ]
326 if $end->{return_value} and not ref $end->{return_value};
327 $event->{return_value} = $end->{return_value};
328 $event->{content} ||= $end->{content};
333 return ($version_status, @lines);
337 RT::Base->_ImportOverlays();