"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;