rt 4.2.15
[freeside.git] / rt / lib / RT / System.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2018 Best Practical Solutions, LLC
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
51 RT::System
52
53 =head1 DESCRIPTION
54
55 RT::System is a simple global object used as a focal point for things
56 that are system-wide.
57
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.
60
61 This gets used by the ACL system so that you can have rights for the scope "RT::System"
62
63 In the future, there will probably be other API goodness encapsulated here.
64
65 =cut
66
67
68 package RT::System;
69
70 use strict;
71 use warnings;
72
73 use base qw/RT::Record/;
74
75 use Role::Basic 'with';
76 with "RT::Record::Role::Roles",
77      "RT::Record::Role::Rights" => { -excludes => [qw/AvailableRights RightCategories/] };
78
79 use RT::ACL;
80 use RT::ACE;
81 use 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
94
95 #freeside
96 __PACKAGE__->AddRight( Staff   => BulkUpdateTickets   => 'Bulk update tickets');
97
98 =head2 AvailableRights
99
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.
102
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
105 globally.
106
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.
111
112 =cut
113
114 sub AvailableRights {
115     my $self = shift;
116     my $principal = shift;
117     my $class = ref($self) || $self;
118
119     my @rights;
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} };
125         }
126     } else {
127         @rights = map {values %{$_}} values %RT::ACE::RIGHTS;
128     }
129
130     my %rights;
131     $rights{$_->{Name}} = $_->{Description} for @rights;
132
133     delete $rights{ExecuteCode} if RT->Config->Get('DisallowExecuteCode');
134
135     return \%rights;
136 }
137
138 =head2 RightCategories
139
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.
142
143 =cut
144
145 sub RightCategories {
146     my $self = shift;
147     my $class = ref($self) || $self;
148
149     my %rights;
150     $rights{$_->{Name}} = $_->{Category}
151         for map {values %{$_}} values %RT::ACE::RIGHTS;
152     return \%rights;
153 }
154
155 sub _Init {
156     my $self = shift;
157     $self->SUPER::_Init (@_) if @_ && $_[0];
158 }
159
160 =head2 id
161
162 Returns RT::System's id. It's 1. 
163
164 =cut
165
166 *Id = \&id;
167 sub id { return 1 }
168
169 sub UID { return "RT::System" }
170
171 =head2 Load
172
173 Since this object is pretending to be an RT::Record, we need a load method.
174 It does nothing
175
176 =cut
177
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 }
184
185 sub SubjectTag {
186     my $self = shift;
187     my $queue = shift;
188
189     use Carp;
190     confess "SubjectTag called on $self with $queue" if $queue;
191
192     return $queue->SubjectTag if $queue;
193
194     my $queues = RT::Queues->new( $self->CurrentUser );
195     $queues->Limit( FIELD => 'SubjectTag', OPERATOR => 'IS NOT', VALUE => 'NULL' );
196     return $queues->DistinctFieldValues('SubjectTag');
197 }
198
199 =head2 QueueCacheNeedsUpdate ( 1 )
200
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.
204
205 If passed a true value, will update the attribute to be the current time.
206
207 =cut
208
209 sub QueueCacheNeedsUpdate {
210     my $self = shift;
211     my $update = shift;
212
213     if ($update) {
214         return $self->SetAttribute(Name => 'QueueCacheNeedsUpdate', Content => time);
215     } else {
216         my $cache = $self->FirstAttribute('QueueCacheNeedsUpdate');
217         return (defined $cache ? $cache->Content : 0 );
218     }
219 }
220
221 =head2 AddUpgradeHistory package, data
222
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
225 a hash reference.
226
227 =cut
228
229 sub AddUpgradeHistory {
230     my $self  = shift;
231     my $package = shift;
232     my $data  = shift;
233
234     $data->{timestamp}  ||= time;
235     $data->{rt_version} ||= $RT::VERSION;
236
237     my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory');
238     my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {};
239
240     push @{ $upgrade_history->{$package} }, $data;
241
242     $self->SetAttribute(
243         Name    => 'UpgradeHistory',
244         Content => $upgrade_history,
245     );
246 }
247
248 =head2 UpgradeHistory [package]
249
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.
253
254 =cut
255
256 sub UpgradeHistory {
257     my $self  = shift;
258     my $package = shift;
259
260     my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory');
261     my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {};
262
263     if ($package) {
264         return @{ $upgrade_history->{$package} || [] };
265     }
266
267     return $upgrade_history;
268 }
269
270 sub ParsedUpgradeHistory {
271     my $self = shift;
272     my $package = shift;
273
274     my $version_status = "Current version: ";
275     if ( $package eq 'RT' ){
276         $version_status .= $RT::VERSION;
277     } elsif ( grep {/$package/} @{RT->Config->Get('Plugins')} ) {
278         no strict 'refs';
279         $version_status .= ${ $package . '::VERSION' };
280     } else {
281         $version_status = "Not currently loaded";
282     }
283
284     my %ids;
285     my @lines;
286
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;
294                 } else {
295                     $event->{individual_id} = Data::GUID->new->as_string;
296                     $event->{full_id} = (@lines ? $lines[-1]{full_id} : Data::GUID->new->as_string);
297                 }
298                 $event->{return_value} = [1] if $event->{stage} eq 'after';
299             }
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;
305                 } else {
306                     push @{ $kids }, $event;
307                 }
308             } else {
309                 push @lines, $event;
310             }
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} };
319                 }
320             } elsif ($ids{$event->{individual_id}}) {
321                 my $end = $event;
322                 $event = $ids{$event->{individual_id}};
323                 $event->{end} = $end->{timestamp};
324
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};
329             }
330         }
331     }
332
333     return ($version_status, @lines);
334 }
335
336
337 RT::Base->_ImportOverlays();
338
339 1;