[Rt-commit] rt branch, 4.4/initial-custom-field, created. rt-4.4.0rc2-27-g2928131

Shawn Moore shawn at bestpractical.com
Wed Apr 20 17:21:53 EDT 2016


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

- Log -----------------------------------------------------------------
commit 60c12a84b12151f16e4454a26ca9ccb11ad7cd38
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 66c3b4c..88b01b5 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 2ebc56b..4effee1 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 66f613a..59cd0ea 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 04bd5a8..66f59c4 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 1149d38..f0b7521 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 83e2b3b..44e3dae 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 ae0262d..de03b30 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 a786a83..48728a2 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 8f3b923..b67043a 100644
--- a/share/html/Articles/Article/Edit.html
+++ b/share/html/Articles/Article/Edit.html
@@ -72,7 +72,8 @@
                               CFContent => \%CFContent, 
                               ClassObj => $ClassObj, 
                               %ARGS,
-                              id =>$id,
+                              id => $id,
+                              ForCreation => ($id eq 'new'),
                               &>
 
 <tr>
diff --git a/share/html/Asset/Create.html b/share/html/Asset/Create.html
index 2b97d7c..498dcdf 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 3c7ba8c..4e96090 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 6881b62..63a8db6 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 dd4105f6a7b8d20f8ef57fc6489ea574c1501bfd
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 4e96090..7e21d31 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 fe7ca40bf2d5bae87b8473b796643467a4a62f58
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 f0b7521..29780f6 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 e95dc7c..1a8054c 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 4579a38..4d0ab34 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 b01e976..145ca34 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 c9c6f3534cf190e24824d1104ca6b934e878fbb0
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 d19f152..824c1ab 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 ab13a43..266ba6e 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 de03b30..8947a91 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 48728a2..b65aae4 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 145ca34..9d1b009 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 2a66732416de5b9bbc4648c3a6e022b5b7de9b1f
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 4d0ab34..70a96ac 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 2928131f083ca86b334a8c89c31043cd6ac2d82e
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 29780f6..af2da9f 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 1a8054c..4cc5961 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 266ba6e..563b09e 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 70a96ac..0909f54 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 8947a91..3018ad8 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 b65aae4..27b186b 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 7e21d31..7349ac2 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..061920d
--- /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