"Fossies" - the Fresh Open Source Software Archive 
Member "RT-Extension-Assets-1.05/lib/RT/Catalog.pm" (15 Oct 2014, 13314 Bytes) of package /linux/misc/RT-Extension-Assets-1.05.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "Catalog.pm" see the
Fossies "Dox" file reference documentation.
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2014 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 use strict;
50 use warnings;
51
52 package RT::Catalog;
53 use base 'RT::Record';
54
55 use Role::Basic 'with';
56 with "RT::Record::Role::Lifecycle",
57 "RT::Record::Role::Roles" => {
58 -rename => {
59 # We provide ACL'd wraps of these.
60 AddRoleMember => "_AddRoleMember",
61 DeleteRoleMember => "_DeleteRoleMember",
62 RoleGroup => "_RoleGroup",
63 },
64 },
65 "RT::Record::Role::Rights";
66
67 require RT::ACE;
68
69 =head1 NAME
70
71 RT::Catalog - A logical set of assets
72
73 =cut
74
75 # For the Lifecycle role
76 sub LifecycleType { "asset" }
77
78 # Setup rights
79 __PACKAGE__->AddRight( General => ShowCatalog => 'See catalogs' ); #loc
80 __PACKAGE__->AddRight( Admin => AdminCatalog => 'Create, modify, and disable catalogs' ); #loc
81
82 __PACKAGE__->AddRight( General => ShowAsset => 'See assets' ); #loc
83 __PACKAGE__->AddRight( Staff => CreateAsset => 'Create assets' ); #loc
84 __PACKAGE__->AddRight( Staff => ModifyAsset => 'Modify assets' ); #loc
85
86 __PACKAGE__->AddRight( General => SeeCustomField => 'View custom field values' ); # loc
87 __PACKAGE__->AddRight( Staff => ModifyCustomField => 'Modify custom field values' ); # loc
88
89 RT::ACE->RegisterCacheHandler(sub {
90 my %args = (
91 Action => "",
92 RightName => "",
93 @_
94 );
95
96 return unless $args{Action} =~ /^(Grant|Revoke)$/i
97 and $args{RightName} =~ /^(ShowCatalog|CreateAsset)$/;
98
99 RT::Catalog->CacheNeedsUpdate(1);
100 });
101
102 =head1 DESCRIPTION
103
104 Catalogs are for assets what queues are for tickets or classes are for
105 articles.
106
107 It announces the rights for assets, and rights are granted at the catalog or
108 global level. Asset custom fields are either applied globally to all Catalogs
109 or individually to specific Catalogs.
110
111 =over 4
112
113 =item id
114
115 =item Name
116
117 Limited to 255 characters.
118
119 =item Description
120
121 Limited to 255 characters.
122
123 =item Lifecycle
124
125 =item Disabled
126
127 =item Creator
128
129 =item Created
130
131 =item LastUpdatedBy
132
133 =item LastUpdated
134
135 =back
136
137 All of these are readable through methods of the same name and mutable through
138 methods of the same name with C<Set> prefixed. The last four are automatically
139 managed.
140
141 =head1 METHODS
142
143 =head2 Load ID or NAME
144
145 Loads the specified Catalog into the current object.
146
147 =cut
148
149 sub Load {
150 my $self = shift;
151 my $id = shift;
152 return unless $id;
153
154 if ( $id =~ /\D/ ) {
155 return $self->LoadByCols( Name => $id );
156 }
157 else {
158 return $self->SUPER::Load($id);
159 }
160 }
161
162 =head2 Create PARAMHASH
163
164 Create takes a hash of values and creates a row in the database. Available keys are:
165
166 =over 4
167
168 =item Name
169
170 =item Description
171
172 =item Lifecycle
173
174 =item HeldBy, Contact
175
176 A single principal ID or array ref of principal IDs to add as members of the
177 respective role groups for the new catalog.
178
179 User Names and EmailAddresses may also be used, but Groups must be referenced
180 by ID.
181
182 =item Disabled
183
184 =back
185
186 Returns a tuple of (status, msg) on failure and (id, msg, non-fatal errors) on
187 success, where the third value is an array reference of errors that occurred
188 but didn't prevent creation.
189
190 =cut
191
192 sub Create {
193 my $self = shift;
194 my %args = (
195 Name => '',
196 Description => '',
197 Lifecycle => 'assets',
198
199 HeldBy => undef,
200 Contact => undef,
201
202 Disabled => 0,
203
204 @_
205 );
206 my @non_fatal_errors;
207
208 return (0, $self->loc("Permission Denied"))
209 unless $self->CurrentUserHasRight('AdminCatalog');
210
211 return (0, $self->loc('Invalid Name (names must be unique and may not be all digits)'))
212 unless $self->ValidateName( $args{'Name'} );
213
214 $args{'Lifecycle'} ||= 'assets';
215
216 return (0, $self->loc('[_1] is not a valid lifecycle', $args{'Lifecycle'}))
217 unless $self->ValidateLifecycle( $args{'Lifecycle'} );
218
219 RT->DatabaseHandle->BeginTransaction();
220
221 my ( $id, $msg ) = $self->SUPER::Create(
222 map { $_ => $args{$_} } qw(Name Description Lifecycle Disabled),
223 );
224 unless ($id) {
225 RT->DatabaseHandle->Rollback();
226 return (0, $self->loc("Catalog create failed: [_1]", $msg));
227 }
228
229 # Create role groups
230 unless ($self->_CreateRoleGroups()) {
231 RT->Logger->error("Couldn't create role groups for catalog ". $self->id);
232 RT->DatabaseHandle->Rollback();
233 return (0, $self->loc("Couldn't create role groups for catalog"));
234 }
235
236 # Figure out users for roles
237 my $roles = {};
238 push @non_fatal_errors, $self->_ResolveRoles( $roles, %args );
239 push @non_fatal_errors, $self->_AddRolesOnCreate( $roles, map { $_ => sub {1} } $self->Roles );
240
241 # Create transaction
242 my ( $txn_id, $txn_msg, $txn ) = $self->_NewTransaction( Type => 'Create' );
243 unless ($txn_id) {
244 RT->DatabaseHandle->Rollback();
245 return (0, $self->loc( 'Catalog Create txn failed: [_1]', $txn_msg ));
246 }
247
248 $self->CacheNeedsUpdate(1);
249 RT->DatabaseHandle->Commit();
250
251 return ($id, $self->loc('Catalog #[_1] created: [_2]', $self->id, $args{'Name'}), \@non_fatal_errors);
252 }
253
254 =head2 ValidateName NAME
255
256 Requires that Names contain at least one non-digit and doesn't already exist.
257
258 =cut
259
260 sub ValidateName {
261 my $self = shift;
262 my $name = shift;
263 return 0 unless defined $name and length $name;
264 return 0 unless $name =~ /\D/;
265
266 my $catalog = RT::Catalog->new( RT->SystemUser );
267 $catalog->Load($name);
268 return 0 if $catalog->id;
269
270 return 1;
271 }
272
273 =head2 Delete
274
275 Catalogs may not be deleted. Always returns failure.
276
277 You should disable the catalog instead using C<< $catalog->SetDisabled(1) >>.
278
279 =cut
280
281 sub Delete {
282 my $self = shift;
283 return (0, $self->loc("Catalogs may not be deleted"));
284 }
285
286 =head2 CurrentUserCanSee
287
288 Returns true if the current user can see the catalog via the I<ShowCatalog> or
289 I<AdminCatalog> rights.
290
291 =cut
292
293 sub CurrentUserCanSee {
294 my $self = shift;
295 return $self->CurrentUserHasRight('ShowCatalog')
296 || $self->CurrentUserHasRight('AdminCatalog');
297 }
298
299 =head2 Owner
300
301 Returns an L<RT::User> object for this catalog's I<Owner> role group. On error,
302 returns undef.
303
304 =head2 HeldBy
305
306 Returns an L<RT::Group> object for this catalog's I<HeldBy> role group. The object
307 may be unloaded if permissions aren't satisfied.
308
309 =head2 Contacts
310
311 Returns an L<RT::Group> object for this catalog's I<Contact> role
312 group. The object may be unloaded if permissions aren't satisfied.
313
314 =cut
315
316 sub Owner {
317 my $self = shift;
318 my $group = $self->RoleGroup("Owner");
319 return unless $group and $group->id;
320 return $group->UserMembersObj->First;
321 }
322 sub HeldBy { $_[0]->RoleGroup("HeldBy") }
323 sub Contacts { $_[0]->RoleGroup("Contact") }
324
325 =head2 AddRoleMember
326
327 Checks I<AdminCatalog> before calling L<RT::Record::Role::Roles/_AddRoleMember>.
328
329 =cut
330
331 sub AddRoleMember {
332 my $self = shift;
333
334 return (0, $self->loc("No permission to modify this catalog"))
335 unless $self->CurrentUserHasRight("AdminCatalog");
336
337 return $self->_AddRoleMember(@_);
338 }
339
340 =head2 DeleteRoleMember
341
342 Checks I<AdminCatalog> before calling L<RT::Record::Role::Roles/_DeleteRoleMember>.
343
344 =cut
345
346 sub DeleteRoleMember {
347 my $self = shift;
348
349 return (0, $self->loc("No permission to modify this catalog"))
350 unless $self->CurrentUserHasRight("AdminCatalog");
351
352 return $self->_DeleteRoleMember(@_);
353 }
354
355 =head2 RoleGroup
356
357 An ACL'd version of L<RT::Record::Role::Roles/_RoleGroup>. Checks I<ShowCatalog>.
358
359 =cut
360
361 sub RoleGroup {
362 my $self = shift;
363 if ($self->CurrentUserCanSee) {
364 return $self->_RoleGroup(@_);
365 } else {
366 return RT::Group->new( $self->CurrentUser );
367 }
368 }
369
370 =head2 AssetCustomFields
371
372 Returns an L<RT::CustomFields> object containing all global and
373 catalog-specific B<asset> custom fields.
374
375 =cut
376
377 sub AssetCustomFields {
378 my $self = shift;
379 my $cfs = RT::CustomFields->new( $self->CurrentUser );
380 if ($self->CurrentUserCanSee) {
381 $cfs->SetContextObject( $self );
382 $cfs->LimitToGlobalOrObjectId( $self->Id );
383 $cfs->LimitToLookupType( RT::Asset->CustomFieldLookupType );
384 $cfs->ApplySortOrder;
385 } else {
386 $cfs->Limit( FIELD => 'id', VALUE => 0, SUBCLAUSE => 'acl' );
387 }
388 return ($cfs);
389 }
390
391 =head1 INTERNAL METHODS
392
393 =head2 CacheNeedsUpdate
394
395 Takes zero or one arguments.
396
397 If a true argument is provided, marks any Catalog caches as needing an update.
398 This happens when catalogs are created, disabled/enabled, or modified. Returns
399 nothing.
400
401 If no arguments are provided, returns an epoch time that any catalog caches
402 should be newer than.
403
404 May be called as a class or object method.
405
406 =cut
407
408 sub CacheNeedsUpdate {
409 my $class = shift;
410 my $update = shift;
411
412 if ($update) {
413 RT->System->SetAttribute(Name => 'CatalogCacheNeedsUpdate', Content => time);
414 return;
415 } else {
416 my $attribute = RT->System->FirstAttribute('CatalogCacheNeedsUpdate');
417 return $attribute ? $attribute->Content : 0;
418 }
419 }
420
421 =head1 PRIVATE METHODS
422
423 Documented for internal use only, do not call these from outside RT::Catalog
424 itself.
425
426 =head2 _Set
427
428 Checks if the current user can I<AdminCatalog> before calling C<SUPER::_Set>
429 and records a transaction against this object if C<SUPER::_Set> was
430 successful.
431
432 =cut
433
434 sub _Set {
435 my $self = shift;
436 my %args = (
437 Field => undef,
438 Value => undef,
439 @_
440 );
441
442 return (0, $self->loc("Permission Denied"))
443 unless $self->CurrentUserHasRight('AdminCatalog');
444
445 my $old = $self->_Value( $args{'Field'} );
446
447 my ($ok, $msg) = $self->SUPER::_Set(@_);
448
449 # Only record the transaction if the _Set worked
450 return ($ok, $msg) unless $ok;
451
452 my $txn_type = "Set";
453 if ($args{'Field'} eq "Disabled") {
454 if (not $old and $args{'Value'}) {
455 $txn_type = "Disabled";
456 }
457 elsif ($old and not $args{'Value'}) {
458 $txn_type = "Enabled";
459 }
460 }
461
462 $self->CacheNeedsUpdate(1);
463
464 my ($txn_id, $txn_msg, $txn) = $self->_NewTransaction(
465 Type => $txn_type,
466 Field => $args{'Field'},
467 NewValue => $args{'Value'},
468 OldValue => $old,
469 );
470 return ($txn_id, scalar $txn->BriefDescription);
471 }
472
473 =head2 _Value
474
475 Checks L</CurrentUserCanSee> before calling C<SUPER::_Value>.
476
477 =cut
478
479 sub _Value {
480 my $self = shift;
481 return unless $self->CurrentUserCanSee;
482 return $self->SUPER::_Value(@_);
483 }
484
485 sub Table { "RTxCatalogs" }
486
487 sub _CoreAccessible {
488 {
489 id => { read => 1, type => 'int(11)', default => '' },
490 Name => { read => 1, type => 'varchar(255)', default => '', write => 1 },
491 Description => { read => 1, type => 'varchar(255)', default => '', write => 1 },
492 Lifecycle => { read => 1, type => 'varchar(32)', default => 'assets', write => 1 },
493 Disabled => { read => 1, type => 'int(2)', default => '0', write => 1 },
494 Creator => { read => 1, type => 'int(11)', default => '0', auto => 1 },
495 Created => { read => 1, type => 'datetime', default => '', auto => 1 },
496 LastUpdatedBy => { read => 1, type => 'int(11)', default => '0', auto => 1 },
497 LastUpdated => { read => 1, type => 'datetime', default => '', auto => 1 },
498 }
499 }
500
501 RT::Base->_ImportOverlays();
502
503 1;