[Rt-commit] rt branch, 4.4/initial-custom-field, created. rt-4.4.0-102-gce92fa2

Dustin Graves dustin at bestpractical.com
Fri May 6 13:22:59 EDT 2016


The branch, 4.4/initial-custom-field has been created
        at  ce92fa285c404b7ad8ee65aa20133969a4631853 (commit)

- Log -----------------------------------------------------------------
commit dca5e8bd2aa2cae4e90978f56b36cd526f93e57f
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Mon Dec 28 02:12:21 2015 +0000

    Add a new SetInitialCustomField right
    
        This lets you set custom field values on records (tickets, assets,
        articles) that you're creating. But it does not permit modifying the
        custom field values of existing records.
    
    Fixes: I#14974

diff --git a/lib/RT/Article.pm b/lib/RT/Article.pm
index e8ea75c..6eb9640 100644
--- a/lib/RT/Article.pm
+++ b/lib/RT/Article.pm
@@ -147,7 +147,8 @@ sub Create {
                     : (Value => $value)
                 ),
                 Field             => $cf,
-                RecordTransaction => 0
+                RecordTransaction => 0,
+                ForCreation       => 1,
             );
 
             unless ($cfid) {
diff --git a/lib/RT/Asset.pm b/lib/RT/Asset.pm
index c9efb08..b2e75bb 100644
--- a/lib/RT/Asset.pm
+++ b/lib/RT/Asset.pm
@@ -293,7 +293,8 @@ sub Create {
                     ? %$value
                     : (Value => $value)),
                 Field             => $cf,
-                RecordTransaction => 0
+                RecordTransaction => 0,
+                ForCreation       => 1,
             );
             unless ($cfid) {
                 RT->DatabaseHandle->Rollback();
diff --git a/lib/RT/Catalog.pm b/lib/RT/Catalog.pm
index 1ed8585..c6b457c 100644
--- a/lib/RT/Catalog.pm
+++ b/lib/RT/Catalog.pm
@@ -83,8 +83,9 @@ __PACKAGE__->AddRight( General => ShowAsset    => 'See assets' ); #loc
 __PACKAGE__->AddRight( Staff   => CreateAsset  => 'Create assets' ); #loc
 __PACKAGE__->AddRight( Staff   => ModifyAsset  => 'Modify assets' ); #loc
 
-__PACKAGE__->AddRight( General => SeeCustomField      => 'View custom field values' ); # loc
-__PACKAGE__->AddRight( Staff   => ModifyCustomField   => 'Modify custom field values' ); # loc
+__PACKAGE__->AddRight( General => SeeCustomField        => 'View custom field values' ); # loc
+__PACKAGE__->AddRight( Staff   => ModifyCustomField     => 'Modify custom field values' ); # loc
+__PACKAGE__->AddRight( Staff   => SetInitialCustomField => 'Add custom field values only at object creation time'); # loc
 
 RT::ACE->RegisterCacheHandler(sub {
     my %args = (
diff --git a/lib/RT/Class.pm b/lib/RT/Class.pm
index 99faf19..71315ab 100644
--- a/lib/RT/Class.pm
+++ b/lib/RT/Class.pm
@@ -84,19 +84,20 @@ sub Load {
     }
 }
 
-__PACKAGE__->AddRight( Staff   => SeeClass            => 'See that this class exists'); # loc
-__PACKAGE__->AddRight( Staff   => CreateArticle       => 'Create articles in this class'); # loc
-__PACKAGE__->AddRight( General => ShowArticle         => 'See articles in this class'); # loc
-__PACKAGE__->AddRight( Staff   => ShowArticleHistory  => 'See changes to articles in this class'); # loc
-__PACKAGE__->AddRight( General => SeeCustomField      => 'View custom field values' ); # loc
-__PACKAGE__->AddRight( Staff   => ModifyArticle       => 'Modify articles in this class'); # loc
-__PACKAGE__->AddRight( Staff   => ModifyArticleTopics => 'Modify topics for articles in this class'); # loc
-__PACKAGE__->AddRight( Staff   => ModifyCustomField   => 'Modify custom field values' ); # loc
-__PACKAGE__->AddRight( Admin   => AdminClass          => 'Modify metadata and custom fields for this class'); # loc
-__PACKAGE__->AddRight( Admin   => AdminTopics         => 'Modify topic hierarchy associated with this class'); # loc
-__PACKAGE__->AddRight( Admin   => ShowACL             => 'Display Access Control List'); # loc
-__PACKAGE__->AddRight( Admin   => ModifyACL           => 'Create, modify and delete Access Control List entries'); # loc
-__PACKAGE__->AddRight( Staff   => DisableArticle      => 'Disable articles in this class'); # loc
+__PACKAGE__->AddRight( Staff   => SeeClass              => 'See that this class exists'); # loc
+__PACKAGE__->AddRight( Staff   => CreateArticle         => 'Create articles in this class'); # loc
+__PACKAGE__->AddRight( General => ShowArticle           => 'See articles in this class'); # loc
+__PACKAGE__->AddRight( Staff   => ShowArticleHistory    => 'See changes to articles in this class'); # loc
+__PACKAGE__->AddRight( General => SeeCustomField        => 'View custom field values' ); # loc
+__PACKAGE__->AddRight( Staff   => ModifyArticle         => 'Modify articles in this class'); # loc
+__PACKAGE__->AddRight( Staff   => ModifyArticleTopics   => 'Modify topics for articles in this class'); # loc
+__PACKAGE__->AddRight( Staff   => ModifyCustomField     => 'Modify custom field values' ); # loc
+__PACKAGE__->AddRight( Staff   => SetInitialCustomField => 'Add custom field values only at object creation time'); # loc
+__PACKAGE__->AddRight( Admin   => AdminClass            => 'Modify metadata and custom fields for this class'); # loc
+__PACKAGE__->AddRight( Admin   => AdminTopics           => 'Modify topic hierarchy associated with this class'); # loc
+__PACKAGE__->AddRight( Admin   => ShowACL               => 'Display Access Control List'); # loc
+__PACKAGE__->AddRight( Admin   => ModifyACL             => 'Create, modify and delete Access Control List entries'); # loc
+__PACKAGE__->AddRight( Staff   => DisableArticle        => 'Disable articles in this class'); # loc
 
 # {{{ Create
 
diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index a6fb1d7..ed8503c 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -214,6 +214,7 @@ __PACKAGE__->AddRight( General => SeeCustomField         => 'View custom fields'
 __PACKAGE__->AddRight( Admin   => AdminCustomField       => 'Create, modify and delete custom fields'); # loc
 __PACKAGE__->AddRight( Admin   => AdminCustomFieldValues => 'Create, modify and delete custom fields values'); # loc
 __PACKAGE__->AddRight( Staff   => ModifyCustomField      => 'Add, modify and delete custom field values for objects'); # loc
+__PACKAGE__->AddRight( Staff   => SetInitialCustomField  => 'Add custom field values only at object creation time'); # loc
 
 =head1 NAME
 
@@ -1639,11 +1640,15 @@ sub AddValueForObject {
         Content      => undef,
         LargeContent => undef,
         ContentType  => undef,
+        ForCreation  => 0,
         @_
     );
     my $obj = $args{'Object'} or return ( 0, $self->loc('Invalid object') );
 
-    unless ( $self->CurrentUserHasRight('ModifyCustomField') ) {
+    unless (
+        $self->CurrentUserHasRight('ModifyCustomField') ||
+        ($args{ForCreation} && $self->CurrentUserHasRight('SetInitialCustomField'))
+    ) {
         return ( 0, $self->loc('Permission Denied') );
     }
 
diff --git a/lib/RT/Queue.pm b/lib/RT/Queue.pm
index 7565b1d..b3940b3 100644
--- a/lib/RT/Queue.pm
+++ b/lib/RT/Queue.pm
@@ -100,16 +100,17 @@ use RT::CustomRoles;
 use RT::ACL;
 use RT::Interface::Email;
 
-__PACKAGE__->AddRight( General => SeeQueue            => 'View queue' ); # loc
-__PACKAGE__->AddRight( Admin   => AdminQueue          => 'Create, modify and delete queue' ); # loc
-__PACKAGE__->AddRight( Admin   => ShowACL             => 'Display Access Control List' ); # loc
-__PACKAGE__->AddRight( Admin   => ModifyACL           => 'Create, modify and delete Access Control List entries' ); # loc
-__PACKAGE__->AddRight( Admin   => ModifyQueueWatchers => 'Modify queue watchers' ); # loc
-__PACKAGE__->AddRight( General => SeeCustomField      => 'View custom field values' ); # loc
-__PACKAGE__->AddRight( Staff   => ModifyCustomField   => 'Modify custom field values' ); # loc
-__PACKAGE__->AddRight( Admin   => AssignCustomFields  => 'Assign and remove queue custom fields' ); # loc
-__PACKAGE__->AddRight( Admin   => ModifyTemplate      => 'Modify Scrip templates' ); # loc
-__PACKAGE__->AddRight( Admin   => ShowTemplate        => 'View Scrip templates' ); # loc
+__PACKAGE__->AddRight( General => SeeQueue              => 'View queue' ); # loc
+__PACKAGE__->AddRight( Admin   => AdminQueue            => 'Create, modify and delete queue' ); # loc
+__PACKAGE__->AddRight( Admin   => ShowACL               => 'Display Access Control List' ); # loc
+__PACKAGE__->AddRight( Admin   => ModifyACL             => 'Create, modify and delete Access Control List entries' ); # loc
+__PACKAGE__->AddRight( Admin   => ModifyQueueWatchers   => 'Modify queue watchers' ); # loc
+__PACKAGE__->AddRight( General => SeeCustomField        => 'View custom field values' ); # loc
+__PACKAGE__->AddRight( Staff   => ModifyCustomField     => 'Modify custom field values' ); # loc
+__PACKAGE__->AddRight( Staff   => SetInitialCustomField => 'Add custom field values only at object creation time'); # loc
+__PACKAGE__->AddRight( Admin   => AssignCustomFields    => 'Assign and remove queue custom fields' ); # loc
+__PACKAGE__->AddRight( Admin   => ModifyTemplate        => 'Modify Scrip templates' ); # loc
+__PACKAGE__->AddRight( Admin   => ShowTemplate          => 'View Scrip templates' ); # loc
 
 __PACKAGE__->AddRight( Admin   => ModifyScrips        => 'Modify Scrips' ); # loc
 __PACKAGE__->AddRight( Admin   => ShowScrips          => 'View Scrips' ); # loc
diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 2d96103..7f04111 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1937,6 +1937,7 @@ sub _AddCustomFieldValue {
         LargeContent      => undef,
         ContentType       => undef,
         RecordTransaction => 1,
+        ForCreation       => 0,
         @_
     );
 
@@ -2015,6 +2016,7 @@ sub _AddCustomFieldValue {
             Content      => $args{'Value'},
             LargeContent => $args{'LargeContent'},
             ContentType  => $args{'ContentType'},
+            ForCreation  => $args{'ForCreation'},
         );
 
         unless ( $new_value_id ) {
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index bd17619..61f0ee0 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -502,6 +502,7 @@ sub Create {
                 ),
                 Field             => $cfid,
                 RecordTransaction => 0,
+                ForCreation       => 1,
             );
             push @non_fatal_errors, $msg unless $status;
         }
diff --git a/share/html/Articles/Article/Edit.html b/share/html/Articles/Article/Edit.html
index f2905e6..36d21e9 100644
--- a/share/html/Articles/Article/Edit.html
+++ b/share/html/Articles/Article/Edit.html
@@ -67,7 +67,8 @@
                               CFContent => \%CFContent,
                               ClassObj => $ClassObj,
                               %ARGS,
-                              id =>$id,
+                              id => $id,
+                              ForCreation => ($id eq 'new'),
                               &>
 </&>
 
diff --git a/share/html/Asset/Create.html b/share/html/Asset/Create.html
index 1d258ce..d059ec9 100644
--- a/share/html/Asset/Create.html
+++ b/share/html/Asset/Create.html
@@ -94,7 +94,9 @@
         Object => $asset,
         TitleBoxARGS => { title_class => "inverse" },
         KeepValue => 1,
-        CustomFieldGenerator => sub { $catalog->AssetCustomFields } &>
+        CustomFieldGenerator => sub { $catalog->AssetCustomFields },
+        ForCreation => 1,
+  &>
 </div>
 
   <& /Elements/Submit, Label => loc('Create asset') &>
diff --git a/share/html/Elements/EditCustomFields b/share/html/Elements/EditCustomFields
index 79174de..4423b0a 100644
--- a/share/html/Elements/EditCustomFields
+++ b/share/html/Elements/EditCustomFields
@@ -51,7 +51,8 @@
 <<% $WRAP %> class="edit-custom-fields">
 % }
 % while ( my $CustomField = $CustomFields->Next ) {
-% next unless $CustomField->CurrentUserHasRight('ModifyCustomField');
+% next unless $CustomField->CurrentUserHasRight('ModifyCustomField')
+%          || ($ForCreation && $CustomField->CurrentUserHasRight('SetInitialCustomField'));
 % my $Type = $CustomField->Type || 'Unknown';
 
   <<% $FIELD %> class="edit-custom-field cftype-<% $Type %>">
@@ -116,4 +117,5 @@ $Grouping     => undef
 $AsTable => 1
 $InTable => 0
 $ShowHints => 1
+$ForCreation => 0
 </%ARGS>
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index 8357af2..e396857 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -121,6 +121,7 @@
           CustomFields => $QueueObj->TicketCustomFields,
           Grouping => 'Basics',
           InTable => 1,
+          ForCreation => 1,
       &>
       <& /Ticket/Elements/EditTransactionCustomFields, %ARGS, QueueObj => $QueueObj, InTable => 1 &>
     </table>
@@ -134,6 +135,7 @@
     %ARGS,
     Object => $ticket,
     CustomFieldGenerator => sub { $QueueObj->TicketCustomFields },
+    ForCreation => 1,
 &>
 
 </div>
@@ -210,6 +212,7 @@
     CustomFields => $QueueObj->TicketCustomFields,
     Grouping => 'People',
     InTable => 1,
+    ForCreation => 1,
 &>
 
 <tr>
@@ -304,6 +307,7 @@
     CustomFields => $QueueObj->TicketCustomFields,
     Grouping => 'Dates',
     InTable => 1,
+    ForCreation => 1,
 &>
 </table>
 </&>

commit 182d10ac59f83b7939e6c77949bf7b6d80f9aae8
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Thu Mar 3 23:19:07 2016 +0000

    Hide empty "edit custom fields" panels
    
        The titlebox widget hides its chrome when its content contains only
        whitespace characters. This commit suppresses all output from
        EditCustomFields when there are no editable custom fields. This causes
        the titlebox to render nothing, rather than a panel with
        chrome but without content.
    
        Beware that the BeforeCustomFields and AfterCustomFields callbacks
        _may_ generate output even when there are no editable custom fields.
        This commit was carefully written to handle such cases by continuing to
        invoke those callbacks unconditionally.
    
        As best I can tell, we have always had this bug surviving through
        several refactorings. The only conditions under which we successfully
        hid "edit custom field" panels is for users who had no rights to _see_
        any custom fields, since that triggers the following long-standing
        short-circuit return:
    
            # don't print anything if there is no custom fields
            return unless $CustomFields->First;
    
        If you can _see_ any custom fields, they are included in the
        $CustomFields collection, causing us to skip right past that
        short-circuit return. Then, we begin rendering with a <div> or <table>
        container tag. We then iterate over $CustomFields, skipping any custom
        fields you have no modify permissions for. Finally we close the
        container tag. So in the failure case, even though there was no
        _visible_ content, there was still some non-whitespace content (the HTML
        of the container tag) being produced, which was enough to cause the
        titlebox to render its chrome.

diff --git a/share/html/Elements/EditCustomFields b/share/html/Elements/EditCustomFields
index 4423b0a..f09fe6c 100644
--- a/share/html/Elements/EditCustomFields
+++ b/share/html/Elements/EditCustomFields
@@ -47,12 +47,18 @@
 %# END BPS TAGGED BLOCK }}}
 % $m->callback( CallbackName => 'BeforeCustomFields', Object => $Object,
 %               Grouping => $Grouping, ARGSRef => \%ARGS, CustomFields => $CustomFields);
+
+%# only show the wrapper if there are editable custom fields, so we can
+%# suppress the empty titlebox. we do this in such a way that we still call the
+%# BeforeCustomFields and AfterCustomFields callbacks (rather than returning
+%# from the INIT block) to maintain compatibility with old behavior
+
+% if (@CustomFields) {
+
 % if ( $WRAP ) {
 <<% $WRAP %> class="edit-custom-fields">
 % }
-% while ( my $CustomField = $CustomFields->Next ) {
-% next unless $CustomField->CurrentUserHasRight('ModifyCustomField')
-%          || ($ForCreation && $CustomField->CurrentUserHasRight('SetInitialCustomField'));
+% for my $CustomField (@CustomFields) {
 % my $Type = $CustomField->Type || 'Unknown';
 
   <<% $FIELD %> class="edit-custom-field cftype-<% $Type %>">
@@ -86,6 +92,9 @@
 % if ( $WRAP ) {
 </<% $WRAP %>>
 % }
+
+% }
+
 % $m->callback( CallbackName => 'AfterCustomFields', Object => $Object,
 %               Grouping => $Grouping, ARGSRef => \%ARGS );
 <%INIT>
@@ -95,9 +104,14 @@ $CustomFields->LimitToGrouping( $Object => $Grouping ) if defined $Grouping;
 
 $m->callback( %ARGS, CallbackName => 'MassageCustomFields', CustomFields => $CustomFields );
 
-# don't print anything if there is no custom fields
-return unless $CustomFields->First;
 $CustomFields->GotoFirstItem;
+my @CustomFields;
+while ( my $CustomField = $CustomFields->Next ) {
+    next unless $CustomField->CurrentUserHasRight('ModifyCustomField')
+             || ($ForCreation && $CustomField->CurrentUserHasRight('SetInitialCustomField'));
+
+    push @CustomFields, $CustomField;
+}
 
 $AsTable ||= $InTable;
 my $FIELD = $AsTable ? 'tr' : 'div';

commit 13016f4b0cfcde0a531385a39d442a1f901f7598
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Wed Apr 20 18:51:26 2016 +0000

    Add a $CF->CurrentUserCanSee method and switch to it
    
        This is to prepare for allowing users to see custom fields for which they
        do not have SeeCustomField, but for those who should still have access
        thanks to SetInitialCustomField

diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index ed8503c..21db16f 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -611,7 +611,7 @@ sub Values {
     my $cf_values = $class->new( $self->CurrentUser );
     $cf_values->SetCustomFieldObject( $self );
     # if the user has no rights, return an empty object
-    if ( $self->id && $self->CurrentUserHasRight( 'SeeCustomField') ) {
+    if ( $self->id && $self->CurrentUserCanSee ) {
         $cf_values->LimitToCustomField( $self->Id );
     } else {
         $cf_values->Limit( FIELD => 'id', VALUE => 0, SUBCLAUSE => 'acl' );
@@ -1050,7 +1050,7 @@ sub _Value {
     return undef unless $self->id;
 
     # we need to do the rights check
-    unless ( $self->CurrentUserHasRight('SeeCustomField') ) {
+    unless ( $self->CurrentUserCanSee ) {
         $RT::Logger->debug(
             "Permission denied. User #". $self->CurrentUser->id
             ." has no SeeCustomField right on CF #". $self->id
@@ -1870,7 +1870,7 @@ sub ValuesForObject {
     my $object = shift;
 
     my $values = RT::ObjectCustomFieldValues->new($self->CurrentUser);
-    unless ($self->id and $self->CurrentUserHasRight('SeeCustomField')) {
+    unless ($self->id and $self->CurrentUserCanSee) {
         # Return an empty object if they have no rights to see
         $values->Limit( FIELD => "id", VALUE => 0, SUBCLAUSE => "ACL" );
         return ($values);
@@ -1882,6 +1882,16 @@ sub ValuesForObject {
     return ($values);
 }
 
+=head2 CurrentUserCanSee
+
+If the user has SeeCustomField they can see this custom field and its details.
++
+=cut
+
+sub CurrentUserCanSee {
+    my $self = shift;
+    return $self->CurrentUserHasRight('SeeCustomField');
+}
 
 =head2 RegisterLookupType LOOKUPTYPE FRIENDLYNAME
 
@@ -1971,7 +1981,7 @@ sub _URLTemplate {
         }
         return ( 1, $self->loc('Updated') );
     } else {
-        unless ( $self->id && $self->CurrentUserHasRight('SeeCustomField') ) {
+        unless ( $self->id && $self->CurrentUserCanSee ) {
             return (undef);
         }
 
@@ -1993,7 +2003,7 @@ sub SetBasedOn {
     $cf->Load( ref $value ? $value->id : $value );
 
     return (0, "Permission Denied")
-        unless $cf->id && $cf->CurrentUserHasRight('SeeCustomField');
+        unless $cf->id && $cf->CurrentUserCanSee;
 
     # XXX: Remove this restriction once we support lists and cascaded selects
     if ( $self->RenderType =~ /List/ ) {
diff --git a/lib/RT/CustomFields.pm b/lib/RT/CustomFields.pm
index d421ead..6aa6d41 100644
--- a/lib/RT/CustomFields.pm
+++ b/lib/RT/CustomFields.pm
@@ -403,7 +403,8 @@ sub AddRecord {
     my ($record) = @_;
 
     $record->SetContextObject( $self->ContextObject );
-    return unless $record->CurrentUserHasRight('SeeCustomField');
+    return unless $record->CurrentUserCanSee;
+
     return $self->SUPER::AddRecord( $record );
 }
 
diff --git a/lib/RT/ObjectCustomFieldValue.pm b/lib/RT/ObjectCustomFieldValue.pm
index 7235ed1..ba781f5 100644
--- a/lib/RT/ObjectCustomFieldValue.pm
+++ b/lib/RT/ObjectCustomFieldValue.pm
@@ -221,7 +221,7 @@ my $re_ip_serialized = qr/$re_ip_sunit(?:\.$re_ip_sunit){3}/;
 sub Content {
     my $self = shift;
 
-    return undef unless $self->CustomFieldObj->CurrentUserHasRight('SeeCustomField');
+    return undef unless $self->CustomFieldObj->CurrentUserCanSee;
 
     my $content = $self->_Value('Content');
     if (   $self->CustomFieldObj->Type eq 'IPAddress'
diff --git a/lib/RT/Transaction.pm b/lib/RT/Transaction.pm
index 372deeb..cd15dd3 100644
--- a/lib/RT/Transaction.pm
+++ b/lib/RT/Transaction.pm
@@ -1423,7 +1423,7 @@ sub CurrentUserCanSee {
         my $cf = RT::CustomField->new( $self->CurrentUser );
         $cf->SetContextObject( $self->Object );
         $cf->Load( $cf_id );
-        return 0 unless $cf->CurrentUserHasRight('SeeCustomField');
+        return 0 unless $cf->CurrentUserCanSee;
     }
 
     # Transactions that might have changed the ->Object's visibility to

commit ff67b7533073659d6aea6bb3de97a94ec4301212
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Wed Apr 20 19:57:29 2016 +0000

    Support passing the CF directly to CustomFieldValueIsEmpty
    
        No reason to reload it if we already have the object

diff --git a/lib/RT/Action/CreateTickets.pm b/lib/RT/Action/CreateTickets.pm
index dd32da7..75b38b1 100644
--- a/lib/RT/Action/CreateTickets.pm
+++ b/lib/RT/Action/CreateTickets.pm
@@ -1140,7 +1140,7 @@ sub UpdateCustomFields {
 
         foreach my $value (@values) {
             next if $ticket->CustomFieldValueIsEmpty(
-                Field => $cf,
+                Field => $CustomFieldObj,
                 Value => $value,
             );
             my ( $val, $msg ) = $ticket->AddCustomFieldValue(
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index f01337c..23dc84d 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -3211,7 +3211,7 @@ sub _ProcessObjectCustomFieldUpdates {
         if ( $arg eq 'AddValue' || $arg eq 'Value' ) {
             foreach my $value (@values) {
                 next if $args{'Object'}->CustomFieldValueIsEmpty(
-                    Field => $cf->id,
+                    Field => $cf,
                     Value => $value,
                 );
                 my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 7f04111..930d044 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -2124,7 +2124,7 @@ sub AddCustomFieldDefaultValues {
         my $values = $cf->DefaultValues( Object => $on || RT->System );
         foreach my $value ( UNIVERSAL::isa( $values => 'ARRAY' ) ? @$values : $values ) {
             next if $self->CustomFieldValueIsEmpty(
-                Field => $cf->id,
+                Field => $cf,
                 Value => $value,
             );
 
@@ -2161,7 +2161,10 @@ sub CustomFieldValueIsEmpty {
     my $value = $args{Value};
     return 1 unless defined $value  && length $value;
 
-    my $cf = $self->LoadCustomFieldByIdentifier( $args{'Field'} );
+    my $cf = ref($args{'Field'})
+           ? $args{'Field'}
+           : $self->LoadCustomFieldByIdentifier( $args{'Field'} );
+
     if ($cf) {
         if ( $cf->Type =~ /^Date(?:Time)?$/ ) {
             my $DateObj = RT::Date->new( $self->CurrentUser );
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index 61f0ee0..07e5c57 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -490,7 +490,7 @@ sub Create {
             UNIVERSAL::isa( $args{$arg} => 'ARRAY' ) ? @{ $args{$arg} } : ( $args{$arg} ) )
         {
             next if $self->CustomFieldValueIsEmpty(
-                Field => $cfid,
+                Field => $cf,
                 Value => $value,
             );
 
diff --git a/lib/RT/Transaction.pm b/lib/RT/Transaction.pm
index cd15dd3..9fc3211 100644
--- a/lib/RT/Transaction.pm
+++ b/lib/RT/Transaction.pm
@@ -1539,7 +1539,7 @@ sub UpdateCustomFields {
           my $value ( UNIVERSAL::isa( $values, 'ARRAY' ) ? @$values : $values )
         {
             next if $self->CustomFieldValueIsEmpty(
-                Field => $cfid,
+                Field => $cf,
                 Value => $value,
             );
             $self->_AddCustomFieldValue(

commit f3cd5f6fd03de149f1c87bc16bc90c60c5b2fb6a
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Wed Apr 20 21:19:56 2016 +0000

    Avoid constantly reloading the OCFV CustomFieldObj
    
        This code was written as though OCFV kept its CF object in cache, but
        it doesn't.

diff --git a/lib/RT/ObjectCustomFieldValue.pm b/lib/RT/ObjectCustomFieldValue.pm
index ba781f5..fa6623c 100644
--- a/lib/RT/ObjectCustomFieldValue.pm
+++ b/lib/RT/ObjectCustomFieldValue.pm
@@ -221,11 +221,13 @@ my $re_ip_serialized = qr/$re_ip_sunit(?:\.$re_ip_sunit){3}/;
 sub Content {
     my $self = shift;
 
-    return undef unless $self->CustomFieldObj->CurrentUserCanSee;
+    my $cf = $self->CustomFieldObj;
+
+    return undef unless $cf->CurrentUserCanSee;
 
     my $content = $self->_Value('Content');
-    if (   $self->CustomFieldObj->Type eq 'IPAddress'
-        || $self->CustomFieldObj->Type eq 'IPAddressRange' )
+    if (   $cf->Type eq 'IPAddress'
+        || $cf->Type eq 'IPAddressRange' )
     {
 
         require Net::IP;
@@ -236,7 +238,7 @@ sub Content {
             $content = Net::IP::ip_compress_address($1, 6);
         }
 
-        return $content if $self->CustomFieldObj->Type eq 'IPAddress';
+        return $content if $cf->Type eq 'IPAddress';
 
         my $large_content = $self->__Value('LargeContent');
         if ( $large_content =~ /^\s*($re_ip_serialized)\s*$/o ) {

commit ce92fa285c404b7ad8ee65aa20133969a4631853
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Wed Apr 20 20:04:02 2016 +0000

    Allow SetInitialCustomFieldValue without SeeCustomField
    
        This allows you to set up permissions such that users can set custom
        fields on initial ticket create but not see them on ticket display,
        nor edit them on ticket modify.
    
        We have to propagate "this is for creation so
        SetInitialCustomFieldValue is enough to see the CF" from the web
        interface down through to custom field rights checking.

diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index 21db16f..e317810 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -1885,12 +1885,22 @@ sub ValuesForObject {
 =head2 CurrentUserCanSee
 
 If the user has SeeCustomField they can see this custom field and its details.
-+
+
+Otherwise, if the user has SetInitialCustomField and this is being used in a
+"create" context, then they can see this custom field and its details. This
+allows you to set up custom fields that are only visible on create pages and
+are then inaccessible.
+
 =cut
 
 sub CurrentUserCanSee {
     my $self = shift;
-    return $self->CurrentUserHasRight('SeeCustomField');
+    return 1 if $self->CurrentUserHasRight('SeeCustomField');
+
+    return 1 if $self->{include_set_initial}
+             && $self->CurrentUserHasRight('SetInitialCustomField');
+
+    return 0;
 }
 
 =head2 RegisterLookupType LOOKUPTYPE FRIENDLYNAME
diff --git a/lib/RT/CustomFields.pm b/lib/RT/CustomFields.pm
index 6aa6d41..7ef55da 100644
--- a/lib/RT/CustomFields.pm
+++ b/lib/RT/CustomFields.pm
@@ -403,6 +403,8 @@ sub AddRecord {
     my ($record) = @_;
 
     $record->SetContextObject( $self->ContextObject );
+    $record->{include_set_initial} = $self->{include_set_initial};
+
     return unless $record->CurrentUserCanSee;
 
     return $self->SUPER::AddRecord( $record );
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 23dc84d..17a73e6 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -3306,6 +3306,7 @@ sub ProcessObjectCustomFieldUpdatesForCreate {
         # we're only interested in new objects, so only look at $id == 0
         for my $cfid (keys %{ $custom_fields{$class}{0} || {} }) {
             my $cf = RT::CustomField->new( $session{'CurrentUser'} );
+            $cf->{include_set_initial} = 1;
             if ($context) {
                 my $system_cf = RT::CustomField->new( RT->SystemUser );
                 $system_cf->LoadById($cfid);
diff --git a/lib/RT/ObjectCustomFieldValue.pm b/lib/RT/ObjectCustomFieldValue.pm
index fa6623c..101f9e9 100644
--- a/lib/RT/ObjectCustomFieldValue.pm
+++ b/lib/RT/ObjectCustomFieldValue.pm
@@ -222,6 +222,7 @@ sub Content {
     my $self = shift;
 
     my $cf = $self->CustomFieldObj;
+    $cf->{include_set_initial} = $self->{include_set_initial};
 
     return undef unless $cf->CurrentUserCanSee;
 
diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 930d044..6d24385 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1942,6 +1942,8 @@ sub _AddCustomFieldValue {
     );
 
     my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'});
+    $cf->{include_set_initial} = 1 if $args{'ForCreation'};
+
     unless ( $cf->Id ) {
         return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) );
     }
@@ -2024,6 +2026,7 @@ sub _AddCustomFieldValue {
         }
 
         my $new_value = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
+        $new_value->{include_set_initial} = 1 if $args{'ForCreation'};
         $new_value->Load( $new_value_id );
 
         # now that adding the new value was successful, delete the old one
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index 07e5c57..3fa0557 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -484,6 +484,7 @@ sub Create {
         next unless $arg =~ /^CustomField-(\d+)$/i;
         my $cfid = $1;
         my $cf = $self->LoadCustomFieldByIdentifier($cfid);
+        $cf->{include_set_initial} = 1;
         next unless $cf->ObjectTypeFromLookupType($cf->__Value('LookupType'))->isa(ref $self);
 
         foreach my $value (
diff --git a/share/html/Elements/EditCustomFields b/share/html/Elements/EditCustomFields
index f09fe6c..fbfb5ed 100644
--- a/share/html/Elements/EditCustomFields
+++ b/share/html/Elements/EditCustomFields
@@ -99,6 +99,7 @@
 %               Grouping => $Grouping, ARGSRef => \%ARGS );
 <%INIT>
 $CustomFields ||= $Object->CustomFields;
+$CustomFields->{include_set_initial} = 1 if $ForCreation;
 
 $CustomFields->LimitToGrouping( $Object => $Grouping ) if defined $Grouping;
 
diff --git a/t/web/cf_set_initial.t b/t/web/cf_set_initial.t
new file mode 100644
index 0000000..2bf81d9
--- /dev/null
+++ b/t/web/cf_set_initial.t
@@ -0,0 +1,89 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+my ($baseurl, $m) = RT::Test->started_ok;
+
+ok $m->login, 'logged in';
+
+my $cf = RT::CustomField->new( RT->SystemUser );
+my ($cfid, $msg) = $cf->Create(
+    Name => 'Test Set Initial CF',
+    Queue => '0',
+    Type => 'FreeformSingle',
+);
+
+my $tester = RT::Test->load_or_create_user( Name => 'tester', Password => '123456' );
+RT::Test->set_rights(
+    { Principal => $tester->PrincipalObj,
+      Right => [qw(SeeQueue ShowTicket CreateTicket)],
+    },
+);
+ok $m->login( $tester->Name, 123456, logout => 1), 'logged in';
+
+diag "check that we have no CF on the create"
+    ." ticket page when user has no SetInitialCustomField right";
+{
+    $m->submit_form(
+        form_name => "CreateTicketInQueue",
+        fields => { Queue => 'General' },
+    );
+    $m->content_lacks('Test Set Initial CF', 'has no CF input');
+
+    my $form = $m->form_name("TicketCreate");
+    my $edit_field = "Object-RT::Ticket--CustomField-$cfid-Value";
+    ok !$form->find_input( $edit_field ), 'no form field on the page';
+
+    $m->submit_form(
+        form_name => "TicketCreate",
+        fields => { Subject => 'test' },
+    );
+    $m->content_like(qr/Ticket \d+ created/, "a ticket is created succesfully");
+
+    $m->content_lacks('Test Set Initial CF', 'has no CF on the page');
+    $m->follow_link( text => 'Custom Fields');
+    $m->content_lacks('Test Set Initial CF', 'has no CF field');
+}
+
+RT::Test->set_rights(
+    { Principal => $tester->PrincipalObj,
+      Right => [qw(SeeQueue ShowTicket CreateTicket SetInitialCustomField)],
+    },
+);
+
+diag "check that we have the CF on the create"
+    ." ticket page when user has SetInitialCustomField but no SeeCustomField";
+{
+    $m->submit_form(
+        form_name => "CreateTicketInQueue",
+        fields => { Queue => 'General' },
+    );
+    $m->content_contains('Test Set Initial CF', 'has CF input');
+
+    my $form = $m->form_name("TicketCreate");
+    my $edit_field = "Object-RT::Ticket--CustomField-$cfid-Value";
+    ok $form->find_input( $edit_field ), 'has form field on the page';
+
+    $m->submit_form(
+        form_name => "TicketCreate",
+        fields => {
+            $edit_field => 'yatta',
+            Subject => 'test 2',
+        },
+    );
+    $m->content_like(qr/Ticket \d+ created/, "a ticket is created succesfully");
+    if (my ($id) = $m->content =~ /Ticket (\d+) created/) {
+        my $ticket = RT::Ticket->new(RT->SystemUser);
+        my ($ok, $msg) = $ticket->Load($id);
+        ok($ok, "loaded ticket $id");
+        is($ticket->Subject, 'test 2', 'subject is correct');
+        is($ticket->FirstCustomFieldValue('Test Set Initial CF'), 'yatta', 'CF was set correctly');
+    }
+
+    $m->content_lacks('Test Set Initial CF', 'has no CF on the page');
+    $m->follow_link( text => 'Custom Fields');
+    $m->content_lacks('Test Set Initial CF', 'has no CF edit field');
+}
+
+undef $m;
+done_testing;

-----------------------------------------------------------------------


More information about the rt-commit mailing list