Upgrade to 4.2.8
[usit-rt.git] / lib / RT / System.pm
CommitLineData
84fb5b46
MKG
1# BEGIN BPS TAGGED BLOCK {{{
2#
3# COPYRIGHT:
4#
3ffc5f4f 5# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
84fb5b46
MKG
6# <sales@bestpractical.com>
7#
8# (Except where explicitly superseded by other copyright notices)
9#
10#
11# LICENSE:
12#
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
16# from www.gnu.org.
17#
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.
22#
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.
28#
29#
30# CONTRIBUTION SUBMISSION POLICY:
31#
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.)
37#
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.
46#
47# END BPS TAGGED BLOCK }}}
48
49=head1 NAME
50
51RT::System
52
53=head1 DESCRIPTION
54
55RT::System is a simple global object used as a focal point for things
56that are system-wide.
57
58It works sort of like an RT::Record, except it's really a single object that has
59an id of "1" when instantiated.
60
61This gets used by the ACL system so that you can have rights for the scope "RT::System"
62
63In the future, there will probably be other API goodness encapsulated here.
64
65=cut
66
67
68package RT::System;
69
70use strict;
71use warnings;
72
73use base qw/RT::Record/;
74
3ffc5f4f
MKG
75use Role::Basic 'with';
76with "RT::Record::Role::Roles",
77 "RT::Record::Role::Rights" => { -excludes => [qw/AvailableRights RightCategories/] };
84fb5b46 78
3ffc5f4f
MKG
79use RT::ACL;
80use RT::ACE;
81use Data::GUID;
82
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
84fb5b46
MKG
94
95=head2 AvailableRights
96
3ffc5f4f
MKG
97Returns a hashref of available rights for this object. The keys are the
98right names and the values are a description of what the rights do.
84fb5b46 99
3ffc5f4f
MKG
100This method as well returns rights of other RT objects, like
101L<RT::Queue> or L<RT::Group>, to allow users to apply those rights
102globally.
84fb5b46 103
3ffc5f4f
MKG
104If an L<RT::Principal> is passed as the first argument, the available
105rights will be limited to ones which make sense for the principal.
106Currently only role groups are supported and rights announced by object
107types to which the role group doesn't apply are not returned.
84fb5b46 108
3ffc5f4f 109=cut
84fb5b46 110
84fb5b46
MKG
111sub AvailableRights {
112 my $self = shift;
3ffc5f4f
MKG
113 my $principal = shift;
114 my $class = ref($self) || $self;
115
116 my @rights;
117 if ($principal and $principal->IsRoleGroup) {
118 my $role = $principal->Object->Name;
119 for my $class (keys %RT::ACE::RIGHTS) {
120 next unless $class->DOES('RT::Record::Role::Roles') and $class->HasRole($role);
121 push @rights, values %{ $RT::ACE::RIGHTS{$class} };
122 }
123 } else {
124 @rights = map {values %{$_}} values %RT::ACE::RIGHTS;
125 }
84fb5b46 126
3ffc5f4f
MKG
127 my %rights;
128 $rights{$_->{Name}} = $_->{Description} for @rights;
84fb5b46 129
84fb5b46
MKG
130 delete $rights{ExecuteCode} if RT->Config->Get('DisallowExecuteCode');
131
3ffc5f4f 132 return \%rights;
84fb5b46
MKG
133}
134
135=head2 RightCategories
136
137Returns a hashref where the keys are rights for this type of object and the
138values are the category (General, Staff, Admin) the right falls into.
139
140=cut
141
142sub RightCategories {
143 my $self = shift;
3ffc5f4f 144 my $class = ref($self) || $self;
84fb5b46 145
3ffc5f4f
MKG
146 my %rights;
147 $rights{$_->{Name}} = $_->{Category}
148 for map {values %{$_}} values %RT::ACE::RIGHTS;
149 return \%rights;
84fb5b46
MKG
150}
151
152sub _Init {
153 my $self = shift;
154 $self->SUPER::_Init (@_) if @_ && $_[0];
155}
156
157=head2 id
158
159Returns RT::System's id. It's 1.
160
161=cut
162
163*Id = \&id;
164sub id { return 1 }
165
3ffc5f4f
MKG
166sub UID { return "RT::System" }
167
84fb5b46
MKG
168=head2 Load
169
170Since this object is pretending to be an RT::Record, we need a load method.
171It does nothing
172
173=cut
174
175sub Load { return 1 }
176sub Name { return 'RT System' }
177sub __Set { return 0 }
178sub __Value { return 0 }
179sub Create { return 0 }
180sub Delete { return 0 }
181
182sub SubjectTag {
183 my $self = shift;
184 my $queue = shift;
185
186 return $queue->SubjectTag if $queue;
187
188 my $queues = RT::Queues->new( $self->CurrentUser );
189 $queues->Limit( FIELD => 'SubjectTag', OPERATOR => 'IS NOT', VALUE => 'NULL' );
190 return $queues->DistinctFieldValues('SubjectTag');
191}
192
193=head2 QueueCacheNeedsUpdate ( 1 )
194
195Attribute to decide when SelectQueue needs to flush the list of queues
196and retrieve new ones. Set when queues are created, enabled/disabled
197and on certain acl changes. Should also better understand group management.
198
199If passed a true value, will update the attribute to be the current time.
200
201=cut
202
203sub QueueCacheNeedsUpdate {
204 my $self = shift;
205 my $update = shift;
206
207 if ($update) {
208 return $self->SetAttribute(Name => 'QueueCacheNeedsUpdate', Content => time);
209 } else {
210 my $cache = $self->FirstAttribute('QueueCacheNeedsUpdate');
211 return (defined $cache ? $cache->Content : 0 );
212 }
213}
214
3ffc5f4f
MKG
215=head2 AddUpgradeHistory package, data
216
217Adds an entry to the upgrade history database. The package can be either C<RT>
218for core RT upgrades, or the fully qualified name of a plugin. The data must be
219a hash reference.
220
221=cut
222
223sub AddUpgradeHistory {
224 my $self = shift;
225 my $package = shift;
226 my $data = shift;
227
228 $data->{timestamp} ||= time;
229 $data->{rt_version} ||= $RT::VERSION;
230
231 my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory');
232 my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {};
233
234 push @{ $upgrade_history->{$package} }, $data;
235
236 $self->SetAttribute(
237 Name => 'UpgradeHistory',
238 Content => $upgrade_history,
239 );
240}
241
242=head2 UpgradeHistory [package]
243
244Returns the entries of RT's upgrade history. If a package is specified, the list
245of upgrades for that package will be returned. Otherwise a hash reference of
246C<< package => [upgrades] >> will be returned.
247
248=cut
249
250sub UpgradeHistory {
251 my $self = shift;
252 my $package = shift;
253
254 my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory');
255 my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {};
256
257 if ($package) {
258 return @{ $upgrade_history->{$package} || [] };
259 }
260
261 return $upgrade_history;
262}
263
264sub ParsedUpgradeHistory {
265 my $self = shift;
266 my $package = shift;
267
268 my $version_status = "Current version: ";
269 if ( $package eq 'RT' ){
270 $version_status .= $RT::VERSION;
271 } elsif ( grep {/$package/} @{RT->Config->Get('Plugins')} ) {
272 no strict 'refs';
273 $version_status .= ${ $package . '::VERSION' };
274 } else {
275 $version_status = "Not currently loaded";
276 }
277
278 my %ids;
279 my @lines;
280
281 my @events = $self->UpgradeHistory( $package );
282 for my $event (@events) {
283 if ($event->{stage} eq 'before' or (($event->{action}||'') eq 'insert' and not $event->{full_id})) {
284 if (not $event->{full_id}) {
285 # For upgrade done in the 4.1 series without GUIDs
286 if (($event->{type}||'') eq 'full upgrade') {
287 $event->{full_id} = $event->{individual_id} = Data::GUID->new->as_string;
288 } else {
289 $event->{individual_id} = Data::GUID->new->as_string;
290 $event->{full_id} = (@lines ? $lines[-1]{full_id} : Data::GUID->new->as_string);
291 }
292 $event->{return_value} = [1] if $event->{stage} eq 'after';
293 }
294 if ($ids{$event->{full_id}}) {
295 my $kids = $ids{$event->{full_id}}{sub_events} ||= [];
296 # Stitch non-"upgrade"s beneath the previous "upgrade"
297 if ( @{$kids} and $event->{action} ne 'upgrade' and $kids->[-1]{action} eq 'upgrade') {
298 push @{ $kids->[-1]{sub_events} }, $event;
299 } else {
300 push @{ $kids }, $event;
301 }
302 } else {
303 push @lines, $event;
304 }
305 $ids{$event->{individual_id}} = $event;
306 } elsif ($event->{stage} eq 'after') {
307 if (not $event->{individual_id}) {
308 if (($event->{type}||'') eq 'full upgrade') {
309 $lines[-1]{end} = $event->{timestamp} if @lines;
310 } elsif (($event->{type}||'') eq 'individual upgrade') {
311 $lines[-1]{sub_events}[-1]{end} = $event->{timestamp}
312 if @lines and @{ $lines[-1]{sub_events} };
313 }
314 } elsif ($ids{$event->{individual_id}}) {
315 my $end = $event;
316 $event = $ids{$event->{individual_id}};
317 $event->{end} = $end->{timestamp};
318
319 $end->{return_value} = [ split ', ', $end->{return_value}, 2 ]
320 if $end->{return_value} and not ref $end->{return_value};
321 $event->{return_value} = $end->{return_value};
322 $event->{content} ||= $end->{content};
323 }
324 }
325 }
326
327 return ($version_status, @lines);
328}
329
330
84fb5b46
MKG
331RT::Base->_ImportOverlays();
332
3331;