Upgrade to 4.0.13
[usit-rt.git] / lib / RT / Class.pm
CommitLineData
84fb5b46
MKG
1# BEGIN BPS TAGGED BLOCK {{{
2#
3# COPYRIGHT:
4#
403d7b0b 5# This software is Copyright (c) 1996-2013 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
49package RT::Class;
50
51use strict;
52use warnings;
53use base 'RT::Record';
54
55
56use RT::System;
57use RT::CustomFields;
58use RT::ACL;
59use RT::Articles;
60use RT::ObjectClass;
61use RT::ObjectClasses;
62
63sub Table {'Classes'}
64
65=head2 Load IDENTIFIER
66
67Loads a class, either by name or by id
68
69=cut
70
71sub Load {
72 my $self = shift;
73 my $id = shift ;
74
75 return unless $id;
76 if ( $id =~ /^\d+$/ ) {
77 $self->SUPER::Load($id);
78 }
79 else {
80 $self->LoadByCols( Name => $id );
81 }
82}
83
84# {{{ This object provides ACLs
85
86use vars qw/$RIGHTS/;
87$RIGHTS = {
88 SeeClass => 'See that this class exists', #loc_pair
89 CreateArticle => 'Create articles in this class', #loc_pair
90 ShowArticle => 'See articles in this class', #loc_pair
91 ShowArticleHistory => 'See changes to articles in this class', #loc_pair
92 ModifyArticle => 'Modify or delete articles in this class', #loc_pair
93 ModifyArticleTopics => 'Modify topics for articles in this class', #loc_pair
94 AdminClass => 'Modify metadata and custom fields for this class', #loc_pair
95 AdminTopics => 'Modify topic hierarchy associated with this class', #loc_pair
96 ShowACL => 'Display Access Control List', #loc_pair
c36a7e1d 97 ModifyACL => 'Create, modify and delete Access Control List entries', #loc_pair
84fb5b46
MKG
98 DeleteArticle => 'Delete articles in this class', #loc_pair
99};
100
101our $RIGHT_CATEGORIES = {
102 SeeClass => 'Staff',
103 CreateArticle => 'Staff',
104 ShowArticle => 'General',
105 ShowArticleHistory => 'Staff',
106 ModifyArticle => 'Staff',
107 ModifyArticleTopics => 'Staff',
108 AdminClass => 'Admin',
109 AdminTopics => 'Admin',
110 ShowACL => 'Admin',
111 ModifyACL => 'Admin',
112 DeleteArticle => 'Staff',
113};
114
115# TODO: This should be refactored out into an RT::ACLedObject or something
116# stuff the rights into a hash of rights that can exist.
117
118# Tell RT::ACE that this sort of object can get acls granted
119$RT::ACE::OBJECT_TYPES{'RT::Class'} = 1;
120
121# TODO this is ripe for a refacor, since this is stolen from Queue
122__PACKAGE__->AddRights(%$RIGHTS);
123__PACKAGE__->AddRightCategories(%$RIGHT_CATEGORIES);
124
125=head2 AddRights C<RIGHT>, C<DESCRIPTION> [, ...]
126
127Adds the given rights to the list of possible rights. This method
128should be called during server startup, not at runtime.
129
130=cut
131
132sub AddRights {
133 my $self = shift;
134 my %new = @_;
135 $RIGHTS = { %$RIGHTS, %new };
136 %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES,
137 map { lc($_) => $_ } keys %new);
138}
139
140=head2 AddRightCategories C<RIGHT>, C<CATEGORY> [, ...]
141
142Adds the given right and category pairs to the list of right categories. This
143method should be called during server startup, not at runtime.
144
145=cut
146
147sub AddRightCategories {
148 my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
149 my %new = @_;
150 $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new };
151}
152
153=head2 AvailableRights
154
155Returns a hash of available rights for this object. The keys are the right names and the values are a description of what t
156he rights do
157
158=cut
159
160sub AvailableRights {
161 my $self = shift;
162 return ($RIGHTS);
163}
164
165sub RightCategories {
166 return $RIGHT_CATEGORIES;
167}
168
169
170# }}}
171
172
173# {{{ Create
174
175=head2 Create PARAMHASH
176
177Create takes a hash of values and creates a row in the database:
178
179 varchar(255) 'Name'.
180 varchar(255) 'Description'.
181 int(11) 'SortOrder'.
182
183=cut
184
185sub Create {
186 my $self = shift;
187 my %args = (
188 Name => '',
189 Description => '',
190 SortOrder => '0',
191 HotList => 0,
192 @_
193 );
194
195 unless (
196 $self->CurrentUser->HasRight(
197 Right => 'AdminClass',
198 Object => $RT::System
199 )
200 )
201 {
202 return ( 0, $self->loc('Permission Denied') );
203 }
204
205 $self->SUPER::Create(
206 Name => $args{'Name'},
207 Description => $args{'Description'},
208 SortOrder => $args{'SortOrder'},
209 HotList => $args{'HotList'},
210 );
211
212}
213
214sub ValidateName {
215 my $self = shift;
216 my $newval = shift;
217
218 return undef unless ($newval);
219 my $obj = RT::Class->new($RT::SystemUser);
220 $obj->Load($newval);
5b0d0914 221 return undef if $obj->id && ( !$self->id || $self->id != $obj->id );
84fb5b46
MKG
222 return $self->SUPER::ValidateName($newval);
223
224}
225
226# }}}
227
228# }}}
229
230# {{{ ACCESS CONTROL
231
232# {{{ sub _Set
233sub _Set {
234 my $self = shift;
235
236 unless ( $self->CurrentUserHasRight('AdminClass') ) {
237 return ( 0, $self->loc('Permission Denied') );
238 }
239 return ( $self->SUPER::_Set(@_) );
240}
241
242# }}}
243
244# {{{ sub _Value
245
246sub _Value {
247 my $self = shift;
248
249 unless ( $self->CurrentUserHasRight('SeeClass') ) {
250 return (undef);
251 }
252
253 return ( $self->__Value(@_) );
254}
255
256# }}}
257
258sub CurrentUserHasRight {
259 my $self = shift;
260 my $right = shift;
261
262 return (
263 $self->CurrentUser->HasRight(
264 Right => $right,
265 Object => ( $self->Id ? $self : $RT::System ),
266 EquivObjects => [ $RT::System, $RT::System ]
267 )
268 );
269
270}
271
272sub ArticleCustomFields {
273 my $self = shift;
274
275
276 my $cfs = RT::CustomFields->new( $self->CurrentUser );
277 if ( $self->CurrentUserHasRight('SeeClass') ) {
278 $cfs->SetContextObject( $self );
279 $cfs->LimitToGlobalOrObjectId( $self->Id );
280 $cfs->LimitToLookupType( RT::Article->CustomFieldLookupType );
281 $cfs->ApplySortOrder;
282 }
283 return ($cfs);
284}
285
286
287=head1 AppliedTo
288
289Returns collection of Queues this Class is applied to.
290Doesn't takes into account if object is applied globally.
291
292=cut
293
294sub AppliedTo {
295 my $self = shift;
296
297 my ($res, $ocfs_alias) = $self->_AppliedTo;
298 return $res unless $res;
299
300 $res->Limit(
301 ALIAS => $ocfs_alias,
302 FIELD => 'id',
303 OPERATOR => 'IS NOT',
304 VALUE => 'NULL',
305 );
306
307 return $res;
308}
309
310=head1 NotAppliedTo
311
312Returns collection of Queues this Class is not applied to.
313
314Doesn't takes into account if object is applied globally.
315
316=cut
317
318sub NotAppliedTo {
319 my $self = shift;
320
321 my ($res, $ocfs_alias) = $self->_AppliedTo;
322 return $res unless $res;
323
324 $res->Limit(
325 ALIAS => $ocfs_alias,
326 FIELD => 'id',
327 OPERATOR => 'IS',
328 VALUE => 'NULL',
329 );
330
331 return $res;
332}
333
334sub _AppliedTo {
335 my $self = shift;
336
337 my $res = RT::Queues->new( $self->CurrentUser );
338
339 $res->OrderBy( FIELD => 'Name' );
340 my $ocfs_alias = $res->Join(
341 TYPE => 'LEFT',
342 ALIAS1 => 'main',
343 FIELD1 => 'id',
344 TABLE2 => 'ObjectClasses',
345 FIELD2 => 'ObjectId',
346 );
347 $res->Limit(
348 LEFTJOIN => $ocfs_alias,
349 ALIAS => $ocfs_alias,
350 FIELD => 'Class',
351 VALUE => $self->id,
352 );
353 return ($res, $ocfs_alias);
354}
355
356=head2 IsApplied
357
358Takes object id and returns corresponding L<RT::ObjectClass>
359record if this Class is applied to the object. Use 0 to check
360if Class is applied globally.
361
362=cut
363
364sub IsApplied {
365 my $self = shift;
366 my $id = shift;
367 return unless defined $id;
368 my $oc = RT::ObjectClass->new( $self->CurrentUser );
369 $oc->LoadByCols( Class=> $self->id, ObjectId => $id,
370 ObjectType => ( $id ? 'RT::Queue' : 'RT::System' ));
371 return undef unless $oc->id;
372 return $oc;
373}
374
375=head2 AddToObject OBJECT
376
377Apply this Class to a single object, to start with we support Queues
378
379Takes an object
380
381=cut
382
383
384sub AddToObject {
385 my $self = shift;
386 my $object = shift;
387 my $id = $object->Id || 0;
388
389 unless ( $object->CurrentUserHasRight('AdminClass') ) {
390 return ( 0, $self->loc('Permission Denied') );
391 }
392
393 my $queue = RT::Queue->new( $self->CurrentUser );
394 if ( $id ) {
395 my ($ok, $msg) = $queue->Load( $id );
396 unless ($ok) {
397 return ( 0, $self->loc('Invalid Queue, unable to apply Class: [_1]',$msg ) );
398 }
399
400 }
401
402 if ( $self->IsApplied( $id ) ) {
403 return ( 0, $self->loc("Class is already applied to [_1]",$queue->Name) );
404 }
405
406 if ( $id ) {
407 # applying locally
408 return (0, $self->loc("Class is already applied Globally") )
409 if $self->IsApplied( 0 );
410 }
411 else {
412 my $applied = RT::ObjectClasses->new( $self->CurrentUser );
413 $applied->LimitToClass( $self->id );
414 while ( my $record = $applied->Next ) {
415 $record->Delete;
416 }
417 }
418
419 my $oc = RT::ObjectClass->new( $self->CurrentUser );
420 my ( $oid, $msg ) = $oc->Create(
421 ObjectId => $id, Class => $self->id,
422 ObjectType => ( $id ? 'RT::Queue' : 'RT::System' ),
423 );
424 return ( $oid, $msg );
425}
426
427
428=head2 RemoveFromObject OBJECT
429
430Remove this class from a single queue object
431
432=cut
433
434sub RemoveFromObject {
435 my $self = shift;
436 my $object = shift;
437 my $id = $object->Id || 0;
438
439 unless ( $object->CurrentUserHasRight('AdminClass') ) {
440 return ( 0, $self->loc('Permission Denied') );
441 }
442
443 my $ocf = $self->IsApplied( $id );
444 unless ( $ocf ) {
445 return ( 0, $self->loc("This class does not apply to that object") );
446 }
447
448 # XXX: Delete doesn't return anything
449 my ( $oid, $msg ) = $ocf->Delete;
450 return ( $oid, $msg );
451}
452
453
454
455=head2 id
456
457Returns the current value of id.
458(In the database, id is stored as int(11).)
459
460
461=cut
462
463
464=head2 Name
465
466Returns the current value of Name.
467(In the database, Name is stored as varchar(255).)
468
469
470
471=head2 SetName VALUE
472
473
474Set Name to VALUE.
475Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
476(In the database, Name will be stored as a varchar(255).)
477
478
479=cut
480
481
482=head2 Description
483
484Returns the current value of Description.
485(In the database, Description is stored as varchar(255).)
486
487
488
489=head2 SetDescription VALUE
490
491
492Set Description to VALUE.
493Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
494(In the database, Description will be stored as a varchar(255).)
495
496
497=cut
498
499
500=head2 SortOrder
501
502Returns the current value of SortOrder.
503(In the database, SortOrder is stored as int(11).)
504
505
506
507=head2 SetSortOrder VALUE
508
509
510Set SortOrder to VALUE.
511Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
512(In the database, SortOrder will be stored as a int(11).)
513
514
515=cut
516
517
518=head2 Disabled
519
520Returns the current value of Disabled.
521(In the database, Disabled is stored as int(2).)
522
523
524
525=head2 SetDisabled VALUE
526
527
528Set Disabled to VALUE.
529Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
530(In the database, Disabled will be stored as a int(2).)
531
532
533=cut
534
535
536=head2 HotList
537
538Returns the current value of HotList.
539(In the database, HotList is stored as int(2).)
540
541
542
543=head2 SetHotList VALUE
544
545
546Set HotList to VALUE.
547Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
548(In the database, HotList will be stored as a int(2).)
549
550
551=cut
552
553
554=head2 Creator
555
556Returns the current value of Creator.
557(In the database, Creator is stored as int(11).)
558
559
560=cut
561
562
563=head2 Created
564
565Returns the current value of Created.
566(In the database, Created is stored as datetime.)
567
568
569=cut
570
571
572=head2 LastUpdatedBy
573
574Returns the current value of LastUpdatedBy.
575(In the database, LastUpdatedBy is stored as int(11).)
576
577
578=cut
579
580
581=head2 LastUpdated
582
583Returns the current value of LastUpdated.
584(In the database, LastUpdated is stored as datetime.)
585
586
587=cut
588
589
590
591sub _CoreAccessible {
592 {
593
594 id =>
595 {read => 1, type => 'int(11)', default => ''},
596 Name =>
597 {read => 1, write => 1, type => 'varchar(255)', default => ''},
598 Description =>
599 {read => 1, write => 1, type => 'varchar(255)', default => ''},
600 SortOrder =>
601 {read => 1, write => 1, type => 'int(11)', default => '0'},
602 Disabled =>
603 {read => 1, write => 1, type => 'int(2)', default => '0'},
604 HotList =>
605 {read => 1, write => 1, type => 'int(2)', default => '0'},
606 Creator =>
607 {read => 1, auto => 1, type => 'int(11)', default => '0'},
608 Created =>
609 {read => 1, auto => 1, type => 'datetime', default => ''},
610 LastUpdatedBy =>
611 {read => 1, auto => 1, type => 'int(11)', default => '0'},
612 LastUpdated =>
613 {read => 1, auto => 1, type => 'datetime', default => ''},
614
615 }
616};
617
618RT::Base->_ImportOverlays();
619
6201;
621