[Rt-commit] rt branch 5.0/dump-initialdata-copy-queues created. rt-5.0.3-64-g827995ee9d

BPS Git Server git at git.bestpractical.com
Thu Jul 28 20:51:43 UTC 2022


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".

The branch, 5.0/dump-initialdata-copy-queues has been created
        at  827995ee9dd168339059a525d9dd4c1bc33644b9 (commit)

- Log -----------------------------------------------------------------
commit 827995ee9dd168339059a525d9dd4c1bc33644b9
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Fri Jul 29 04:19:49 2022 +0800

    Test copying queue config via rt-dump-initialdata
    
    It covers queue itself, ACL, watchers, scrips, templates, custom fields,
    custom roles and also article classes.

diff --git a/t/api/initialdata_copy_queue.t b/t/api/initialdata_copy_queue.t
new file mode 100644
index 0000000000..694a4c82c5
--- /dev/null
+++ b/t/api/initialdata_copy_queue.t
@@ -0,0 +1,261 @@
+use warnings;
+use strict;
+
+use RT::Test tests => undef;
+RT->Config->Set( 'InitialdataFormatHandlers' => [ 'perl', 'RT::Initialdata::JSON' ] );
+
+my $general = RT::Test->load_or_create_queue( Name => 'General' );
+my $queue   = RT::Test->load_or_create_queue( Name => 'Test' );
+
+my $user     = RT::Test->load_or_create_user( Name => 'alice' );
+my $group    = RT::Test->load_or_create_group('Duty');
+my $engineer = RT::CustomRole->new( RT->SystemUser );
+ok(
+    $engineer->Create(
+        Name      => 'Engineer',
+        MaxValues => 0,
+    )
+);
+ok( $engineer->AddToObject( $_->id ) ) for $general, $queue;
+my $class = RT::Class->new( RT->SystemUser );
+ok( $class->Create( Name => 'FAQ' ) );
+ok( $class->AddToObject($_) ) for $general, $queue;
+
+ok( $queue->AddWatcher(Type => 'AdminCc', PrincipalId => $user->Id ) );
+ok( $queue->AddWatcher(Type => 'Cc', PrincipalId => $group->Id ) );
+
+my $scrip = RT::Scrip->new( RT->SystemUser );
+ok(
+    $scrip->Create(
+        Description    => 'On Comment Notify Owner',
+        ScripCondition => 'On Comment',
+        ScripAction    => 'Notify Owner',
+        Template       => 'Blank',
+    )
+);
+
+ok( $scrip->AddToObject( $_->id ) ) for $general, $queue;
+
+my $template = RT::Template->new( RT->SystemUser );
+ok( $template->Create( Name => 'Foo', Description => 'Foo Description', Queue => $queue->Id, Content => q{Foo} ) );
+
+my $custom_field = RT::Test->load_or_create_custom_field(
+    Name  => 'Action',
+    Type  => 'FreeformSingle',
+    Queue => $queue->Id,
+);
+ok( $custom_field->SetDefaultValues( Object => $queue, Values => 'review, merge' ) );
+
+
+RT::Test->add_rights(
+    { Principal => 'Everyone',           Right => 'SeeQueue',     Object => RT->System },
+    { Principal => 'Requestor',          Right => 'ShowTicket',   Object => $general },
+    { Principal => $group,               Right => 'TakeTicket',   Object => RT->System },
+    { Principal => $user,                Right => 'StealTicket',  Object => $general },
+    { Principal => $engineer->GroupType, Right => 'CreateTicket', Object => RT->System },
+
+    { Principal => 'Privileged',         Right => 'ReplyToTicket',   Object => $queue },
+    { Principal => 'AdminCc',            Right => 'CommentOnTicket', Object => $queue },
+    { Principal => $user,                Right => 'SeeCustomField',  Object => $queue },
+    { Principal => $group,               Right => 'ModifyTicket',    Object => $queue },
+    { Principal => $engineer->GroupType, Right => 'OwnTicket',       Object => $queue },
+);
+
+my $parent_dir = RT->Config->Get('LogDir');
+my $global_dir = File::Spec->catdir( $parent_dir, 'global' );
+my $queue_dir  = File::Spec->catdir( $parent_dir, 'queue' );
+
+diag "Export queue Test";
+
+ok(
+    RT::Test->run_singleton_command(
+        'sbin/rt-dump-initialdata', '--quiet', '--dir', $global_dir, '--sync', '--no-queues',
+    ),
+    'Dump global initialdata'
+);
+
+ok(
+    RT::Test->run_singleton_command(
+        'sbin/rt-dump-initialdata', '--quiet', '--dir', $queue_dir, '--sync', '--limit-queues', 'Test', '--base',
+        File::Spec->catfile( $global_dir, 'initialdata.json' ),
+    ),
+    'Dump Test queue changes'
+);
+
+my $changes;
+{
+    open my $fh, "<" . File::Spec->catfile( $queue_dir, 'changes.json' ) or die "Can't load changes.json";
+    local $/;
+    $changes = <$fh>;
+}
+
+my $expected_changes = JSON::decode_json(<<'EOF');
+{
+   "ACL" : [
+      {
+         "GroupDomain" : "SystemInternal",
+         "GroupType" : "Privileged",
+         "ObjectId" : "Test",
+         "ObjectType" : "RT::Queue",
+         "RightName" : "ReplyToTicket"
+      },
+      {
+         "GroupDomain" : "RT::Queue-Role",
+         "GroupType" : "AdminCc",
+         "ObjectId" : "Test",
+         "ObjectType" : "RT::Queue",
+         "RightName" : "CommentOnTicket"
+      },
+      {
+         "ObjectId" : "Test",
+         "ObjectType" : "RT::Queue",
+         "RightName" : "SeeCustomField",
+         "UserId" : "alice"
+      },
+      {
+         "GroupDomain" : "UserDefined",
+         "GroupId" : "Duty",
+         "ObjectId" : "Test",
+         "ObjectType" : "RT::Queue",
+         "RightName" : "ModifyTicket"
+      },
+      {
+         "GroupDomain" : "RT::Queue-Role",
+         "GroupType" : "RT::CustomRole-Engineer",
+         "ObjectId" : "Test",
+         "ObjectType" : "RT::Queue",
+         "RightName" : "OwnTicket"
+      }
+   ],
+   "Attributes" : [
+      {
+         "Content" : {
+            "Action" : "review, merge"
+         },
+         "ContentType" : "storable",
+         "Name" : "CustomFieldDefaultValues",
+         "Object" : "Test",
+         "ObjectType" : "RT::Queue"
+      }
+   ],
+   "Classes" : [
+      {
+         "ApplyTo" : [
+            "Test"
+         ],
+         "_Original" : {
+            "ApplyTo" : [],
+            "Description" : "",
+            "Name" : "FAQ",
+            "SortOrder" : 0
+         },
+         "_Updated" : 1
+      }
+   ],
+   "CustomFields" : [
+      {
+         "ApplyTo" : [
+            "Test"
+         ],
+         "_Original" : {
+            "ApplyTo" : [],
+            "Description" : "",
+            "EntryHint" : "Enter one value",
+            "LookupType" : "RT::Queue-RT::Ticket",
+            "MaxValues" : 1,
+            "Name" : "Action",
+            "SortOrder" : 0,
+            "Type" : "Freeform"
+         },
+         "_Updated" : 1
+      }
+   ],
+   "CustomRoles" : [
+      {
+         "ApplyTo" : [
+            "Test"
+         ],
+         "_Original" : {
+            "Description" : "",
+            "EntryHint" : "",
+            "MaxValues" : 0,
+            "Name" : "Engineer"
+         },
+         "_Updated" : 1
+      }
+   ],
+   "Members" : [
+      {
+         "Class" : "RT::User",
+         "Group" : "AdminCc",
+         "GroupDomain" : "RT::Queue-Role",
+         "GroupInstance" : "Test",
+         "Name" : "alice"
+      },
+      {
+         "Class" : "RT::Group",
+         "Group" : "Cc",
+         "GroupDomain" : "RT::Queue-Role",
+         "GroupInstance" : "Test",
+         "Name" : "Duty"
+      }
+   ],
+   "Queues" : [
+      {
+         "CommentAddress" : "",
+         "CorrespondAddress" : "",
+         "Description" : "",
+         "Lifecycle" : "default",
+         "Name" : "Test",
+         "SLADisabled" : 1,
+         "SortOrder" : 0
+      }
+   ],
+   "Templates" : [
+      {
+         "Content" : "Foo",
+         "Description" : "Foo Description",
+         "Name" : "Foo",
+         "Queue" : "Test",
+         "Type" : "Perl"
+      }
+   ]
+}
+EOF
+
+is_deeply( JSON::decode_json($changes), $expected_changes, 'Generated changes look good' );
+
+diag "Import queue Test with a new name";
+$changes =~ s/"Test"/"Test2"/g;
+
+open my $fh, '>', File::Spec->catdir( $parent_dir, 'queue_changes.json' );
+print $fh $changes;
+close $fh;
+
+ok(
+    RT->DatabaseHandle->InsertData(
+        File::Spec->catdir( $parent_dir, 'queue_changes.json' ),
+        undef, disconnect_after => 0
+    )
+);
+
+$queue_dir = File::Spec->catdir( $parent_dir, 'queue2' );
+
+ok(
+    RT::Test->run_singleton_command(
+        'sbin/rt-dump-initialdata', '--quiet', '--dir', $queue_dir, '--sync', '--limit-queues', 'Test2', '--base',
+        File::Spec->catfile( $global_dir, 'initialdata.json' ),
+    ),
+    'Dump Test2 queue changes'
+);
+
+my $new_changes;
+{
+    open my $fh, "<" . File::Spec->catfile( $queue_dir, 'changes.json' ) or die "Can't load changes.json";
+    local $/;
+    $new_changes = <$fh>;
+}
+
+is_deeply( JSON::decode_json($new_changes), JSON::decode_json($changes), 'Generated changes look good' );
+
+done_testing;

commit 67ac0157a775171067a533d0cfae58f2ae3d2cd0
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Jul 28 22:35:40 2022 +0800

    Support to dump and import CustomFieldDefaultValues attributes with cf name
    
    The content in db is like:
    
        { 2 => 'value1', 3 => 'value2' }
    
    This commit tweaks it to:
    
        { foo => 'value1', bar => 'value2' }

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index e7f27404a1..549eb6ea75 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -3172,6 +3172,23 @@ sub _CanonilizeAttributeContent {
             }
         }
     }
+    elsif ( $item->{Name} eq 'CustomFieldDefaultValues' ) {
+        my %value;
+        for my $name ( keys %{ $item->{Content} || {} } ) {
+            my $custom_field = RT::CustomField->new( RT->SystemUser );
+            $custom_field->LoadByName(
+                Name          => $name,
+                IncludeGlobal => 1,
+                ObjectType    => $item->{ObjectType},
+                ObjectId      => $item->{ObjectId},
+            );
+
+            if ( $custom_field->Id ) {
+                $value{ $custom_field->Id } = $item->{Content}{$name};
+            }
+        }
+        $item->{Content} = \%value;
+    }
 }
 
 sub _CanonilizeObjectCustomFieldValue {
diff --git a/lib/RT/Migrate/Serializer/JSON.pm b/lib/RT/Migrate/Serializer/JSON.pm
index dabd595003..3f03a70f2f 100644
--- a/lib/RT/Migrate/Serializer/JSON.pm
+++ b/lib/RT/Migrate/Serializer/JSON.pm
@@ -541,6 +541,17 @@ sub CanonicalizeAttributes {
                         }
                     }
                 }
+                elsif ( $record->{Name} eq 'CustomFieldDefaultValues' ) {
+                    my %value;
+                    for my $id ( keys %{ $record->{Content} || {} } ) {
+                        my $custom_field = RT::CustomField->new( RT->SystemUser );
+                        $custom_field->Load($id);
+                        if ( $custom_field->Id ) {
+                            $value{ $custom_field->Name } = $record->{Content}{$id};
+                        }
+                    }
+                    $record->{Content} = \%value;
+                }
             }
         }
         elsif ( $record->{Name} =~ /DefaultDashboard$/ ) {

commit 4cdd421158b8076d4f7870d6b1ec106b2092894a
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Jul 28 22:04:07 2022 +0800

    Support to dump and import custom role ACL with custom role name
    
    This is for custom role groups like "RT::CustomRole-1", this commit
    tweaks it to something like "RT::CustomRole-Enginner".

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index 15b16a7735..e7f27404a1 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -1554,10 +1554,31 @@ sub InsertData {
                   $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
                 } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
                   $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
-                } elsif ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
-                  $princ->LoadRoleGroup( Object => RT->System, Name => $item->{'GroupType'} );
                 } elsif ( $item->{'GroupDomain'} =~ /-Role$/ ) {
-                  $princ->LoadRoleGroup( Object => $object, Name => $item->{'GroupType'} );
+                    my $obj;
+                    if ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
+                        $obj = RT->System;
+                    }
+                    else {
+                        $obj = $object;
+                    }
+
+                    if ( $item->{GroupType} =~ /^RT::CustomRole-(.+)$/ ) {
+                        my $id = $1;
+
+                        # $id could be Name
+                        if ( $id =~ /\D/ ) {
+                            my $custom_role = RT::CustomRole->new( RT->SystemUser );
+                            $custom_role->Load($id);
+                            if ( $custom_role->Id ) {
+                                $item->{GroupType} = $custom_role->GroupType;
+                            }
+                            else {
+                                RT->Logger->error("Unable to load custom role $id");
+                            }
+                        }
+                    }
+                    $princ->LoadRoleGroup( Object => $obj, Name => $item->{'GroupType'} );
                 } else {
                   $princ->Load( $item->{'GroupId'} );
                 }
@@ -2950,12 +2971,13 @@ sub _LoadObject {
             if ( $values->{_Original}{'GroupDomain'} eq 'SystemInternal' ) {
                 $group->LoadSystemInternalGroup( $values->{_Original}{GroupType} );
             }
-            elsif ( $values->{_Original}{'GroupDomain'} eq 'RT::System-Role' ) {
-                $group->LoadRoleGroup( Object => RT->System, Name => $values->{_Original}{GroupType} );
-            }
             elsif ( $values->{_Original}{'GroupDomain'} =~ /-Role$/ ) {
                 my $object;
-                if ( $values->{_Original}{ObjectType} and $values->{_Original}{ObjectId} ) {
+
+                if ( $values->{_Original}{'GroupDomain'} eq 'RT::System-Role' ) {
+                    $object = RT->System;
+                }
+                elsif ( $values->{_Original}{ObjectType} and $values->{_Original}{ObjectId} ) {
                     $object = $values->{_Original}{ObjectType}->new( RT->SystemUser );
                     my ( $ok, $msg ) = $object->Load( $values->{_Original}{ObjectId} );
                     unless ($ok) {
@@ -2969,6 +2991,20 @@ sub _LoadObject {
                 else {
                     $object = RT->System;
                 }
+                if ( $values->{_Original}{GroupType} =~ /^RT::CustomRole-(.+)$/ ) {
+                    my $id = $1;
+                    # $id could be Name
+                    if ( $id =~ /\D/ ) {
+                        my $custom_role = RT::CustomRole->new(RT->SystemUser);
+                        $custom_role->Load($id);
+                        if ( $custom_role->Id ) {
+                            $values->{_Original}{GroupType} = $custom_role->GroupType;
+                        }
+                        else {
+                            RT->Logger->error("Unable to load custom role $id");
+                        }
+                    }
+                }
                 $group->LoadRoleGroup( Object => $object, Name => $values->{_Original}{GroupType} );
             }
 
diff --git a/lib/RT/Migrate/Serializer/JSON.pm b/lib/RT/Migrate/Serializer/JSON.pm
index 78c008c665..dabd595003 100644
--- a/lib/RT/Migrate/Serializer/JSON.pm
+++ b/lib/RT/Migrate/Serializer/JSON.pm
@@ -318,7 +318,12 @@ sub CanonicalizeACLs {
                     $ace->{GroupId} = $group->Name;
                 }
                 if ($domain eq 'SystemInternal' || $domain =~ /-Role$/) {
-                    $ace->{GroupType} = $group->Name;
+                    if ( $group->Name =~ /^RT::CustomRole-\d+$/ ) {
+                        $ace->{GroupType} = 'RT::CustomRole-' . $group->_CustomRoleObj->Name;
+                    }
+                    else {
+                        $ace->{GroupType} = $group->Name;
+                    }
                 }
             }
         }

commit 2d92af4fe50c1096d3031eaceea71b6edab74737
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Jul 23 02:59:51 2022 +0800

    Add --limit-queues and --no-queues support for rt-dump-initialdata
    
    --limit-queues is simply a clone from rt-serializer, --no-queues is to
    export configs without any queue specific ones.

diff --git a/sbin/rt-dump-initialdata.in b/sbin/rt-dump-initialdata.in
index 98a30648b2..e443d7c76d 100644
--- a/sbin/rt-dump-initialdata.in
+++ b/sbin/rt-dump-initialdata.in
@@ -105,6 +105,8 @@ GetOptions(
     "scrips!",
     "acls!",
     "assets!",
+    'queues!',
+    "limit-queues=s@",
 
     "sync",
 
@@ -136,6 +138,26 @@ $args{Sync} = 1 if $OPT{sync} || $OPT{base};
 $args{GC}   = defined $OPT{gc}   ? $OPT{gc}   : 5000;
 $args{Page} = defined $OPT{page} ? $OPT{page} : 100;
 
+if ( defined $OPT{'queues'} ) {
+    $args{Queues} = [] unless $OPT{'queues'};
+}
+elsif ( $OPT{'limit-queues'} ) {
+    my @queue_ids;
+
+    for my $name ( split ',', join ',', @{ $OPT{'limit-queues'} } ) {
+        $name =~ s/^\s+//;
+        $name =~ s/\s+$//;
+        my $queue = RT::Queue->new( RT->SystemUser );
+        $queue->Load($name);
+        if ( !$queue->Id ) {
+            die "Unable to load queue '$name'";
+        }
+        push @queue_ids, $queue->Id;
+    }
+
+    $args{Queues} = \@queue_ids;
+}
+
 my $walker;
 
 my $gnuplot = `which gnuplot`;
@@ -467,6 +489,15 @@ skips them.
 
 By default, all ACLs are dumped; passing C<--no-acls> skips them.
 
+=item B<--no-queues>
+
+By default, all queues are dumped; passing C<--no-queues> skips them.
+
+=item B<--limit-queues>
+
+Takes a list of queue IDs or names separated by commas. When provided, only
+that set of queues will be dumped.
+
 =item B<--sync>
 
 By default, record ids are ordinarily excluded. Pass C<--sync> to

commit 8fa64ea1e1b974daa36b2bae6542f9fdec35ec1f
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Jul 28 02:28:31 2022 +0800

    Handle ObjectClass updates for article classes in initialdata

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index 177b9e5ce1..15b16a7735 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2660,7 +2660,7 @@ sub _UpdateObject {
                 next;
             }
         }
-        if ( $class eq 'RT::CustomRole' ) {
+        elsif ( $class eq 'RT::CustomRole' ) {
             if ( $field eq 'ApplyTo' ) {
                 my %current;
                 my %new;
@@ -2711,6 +2711,61 @@ sub _UpdateObject {
                 next;
             }
         }
+        elsif ( $class eq 'RT::Class' ) {
+            if ( $field eq 'ApplyTo' ) {
+                my %current;
+                my %new;
+
+                # Calculate changes based on $original if possible
+                if ( defined $original->{ApplyTo} ) {
+                    for my $item ( @{ $original->{ApplyTo} } ) {
+                        my $queue = RT::Queue->new( RT->SystemUser );
+                        $queue->Load($item);
+                        if ( $queue->Id ) {
+                            $current{ $queue->Id } = 1;
+                        }
+                    }
+                }
+                else {
+                    my $ocs = RT::ObjectClasses->new( RT->SystemUser );
+                    $ocs->LimitToClass( $object->id );
+
+                    while ( my $oc = $ocs->Next ) {
+                        $current{ $oc->ObjectId } = 1;
+                    }
+                }
+
+
+                for my $item ( @{ $value || [] } ) {
+                    my $queue = RT::Queue->new( RT->SystemUser );
+                    $queue->Load($item);
+                    if ( $queue->Id ) {
+                        $new{ $queue->Id } = 1;
+                    }
+                }
+
+                for my $id ( keys %current ) {
+                    next if $new{$id};
+                    my $queue = RT::Queue->new( RT->SystemUser );
+                    $queue->Load($id);
+                    my ( $ret, $msg ) = $object->RemoveFromObject( $queue );
+                    if ( !$ret ) {
+                        RT->Logger->error( "Couldn't remove Class #" . $object->Id . " from Queue #$id: $msg" );
+                    }
+                }
+
+                for my $id ( keys %new ) {
+                    next if $current{$id};
+                    my $queue = RT::Queue->new( RT->SystemUser );
+                    $queue->Load($id);
+                    my ( $ret, $msg ) = $object->AddToObject($queue);
+                    if ( !$ret ) {
+                        RT->Logger->error( "Couldn't add Class #" . $object->Id . " to Queue #$id: $msg" );
+                    }
+                }
+                next;
+            }
+        }
         elsif ( $class eq 'RT::Scrip' ) {
             if ( $field eq 'Queue' ) {
                 my %current;

commit 1ce0d06608eedf28e0e406fb72328fb95be88e48
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Jul 28 02:17:10 2022 +0800

    Handle ObjectCustomRole updates for custom roles in initialdata

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index 954ceb010c..177b9e5ce1 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2660,6 +2660,57 @@ sub _UpdateObject {
                 next;
             }
         }
+        if ( $class eq 'RT::CustomRole' ) {
+            if ( $field eq 'ApplyTo' ) {
+                my %current;
+                my %new;
+
+                # Calculate changes based on $original if possible
+                if ( defined $original->{ApplyTo} ) {
+                    for my $item ( @{ $original->{ApplyTo} } ) {
+                        my $queue = RT::Queue->new( RT->SystemUser );
+                        $queue->Load($item);
+                        if ( $queue->Id ) {
+                            $current{ $queue->Id } = 1;
+                        }
+                    }
+                }
+                else {
+                    my $ocrs = RT::ObjectCustomRoles->new( RT->SystemUser );
+                    $ocrs->LimitToCustomRole( $object->id );
+
+                    while ( my $ocr = $ocrs->Next ) {
+                        $current{ $ocr->ObjectId } = 1;
+                    }
+                }
+
+
+                for my $item ( @{ $value || [] } ) {
+                    my $queue = RT::Queue->new( RT->SystemUser );
+                    $queue->Load($item);
+                    if ( $queue->Id ) {
+                        $new{ $queue->Id } = 1;
+                    }
+                }
+
+                for my $id ( keys %current ) {
+                    next if $new{$id};
+                    my ($ret, $msg) = $object->RemoveFromObject($id);
+                    if ( !$ret ) {
+                        RT->Logger->error( "Couldn't remove CustomRole #" . $object->Id . " from Queue #$id: $msg" );
+                    }
+                }
+
+                for my $id ( keys %new ) {
+                    next if $current{$id};
+                    my ($ret, $msg) = $object->AddToObject($id);
+                    if ( !$ret ) {
+                        RT->Logger->error( "Couldn't add CustomRole #" . $object->id . " to Queue #$id: $msg" );
+                    }
+                }
+                next;
+            }
+        }
         elsif ( $class eq 'RT::Scrip' ) {
             if ( $field eq 'Queue' ) {
                 my %current;

commit 9fe1355ba955b645ebb5aaab3ee04900a369db9a
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Sep 22 03:07:57 2021 +0800

    Handle ObjectScrip updates for scrips in initialdata

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index 2a7977c46b..954ceb010c 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2517,8 +2517,12 @@ sub _UpdateObject {
         }
     }
 
-    for my $field ( sort { $a eq 'ApplyTo' || $b eq 'ApplyTo' ? 1 : 0 } keys %$values ) {
+    my %order = (
+        'Queue'   => 1,
+        'ApplyTo' => 1,
+    );
 
+    for my $field ( sort { ( $order{$a} || 0 ) <=> ( $order{b} || 0 ) } keys %$values ) {
         if ( $class eq 'RT::Attribute' ) {
             if ( $field eq 'Content' ) {
                 $self->_CanonilizeAttributeContent( $values );
@@ -2656,6 +2660,98 @@ sub _UpdateObject {
                 next;
             }
         }
+        elsif ( $class eq 'RT::Scrip' ) {
+            if ( $field eq 'Queue' ) {
+                my %current;
+                my %new;
+
+                # Calculate changes based on $original if possible
+                if ( defined $original->{Queue} ) {
+                    for my $item ( @{$original->{Queue}} ) {
+                        # Globally applied
+                        if ( $item->{ObjectId} eq 0 ) {
+                            $current{ $item->{Stage} }{0} = $item->{SortOrder};
+                        }
+                        else {
+                            my $queue = RT::Queue->new( RT->SystemUser );
+                            $queue->Load( $item->{ObjectId} );
+                            if ( $queue->id ) {
+                                $current{ $item->{Stage} }{ $queue->id } = $item->{SortOrder};
+                            }
+                        }
+                    }
+                }
+                else {
+                    my $object_scrips = RT::ObjectScrips->new(RT->SystemUser);
+                    $object_scrips->LimitToScrip($object->id);
+
+                    while ( my $object_scrip = $object_scrips->Next ) {
+                        $current{$object_scrip->Stage}{$object_scrip->ObjectId} = $object_scrip->SortOrder;
+                    }
+                }
+
+                for my $item ( @{ $value || [] } ) {
+                    if ( $item->{ObjectId} eq 0 ) {
+                        $new{ $item->{Stage} }{0} = $item->{SortOrder};
+                    }
+                    else {
+                        my $queue = RT::Queue->new( RT->SystemUser );
+                        $queue->Load( $item->{ObjectId} );
+                        if ( $queue->id ) {
+                            $new{ $item->{Stage} }{ $queue->id } = $item->{SortOrder};
+                        }
+                    }
+                }
+
+                for my $stage ( sort keys %current ) {
+                    for my $id ( sort { $current{$stage}{$a} <=> $current{$stage}{$b} } keys %{ $current{$stage} } ) {
+                        my $object_scrip = RT::ObjectScrip->new( RT->SystemUser );
+                        $object_scrip->LoadByCols( Scrip => $object->id, ObjectId => $id, Stage => $stage );
+                        if ( $object_scrip->id ) {
+                            if ( defined $new{$stage}{$id} ) {
+                                if ( $new{$stage}{$id} != $current{$stage}{$id} ) {
+                                    my ( $ret, $msg ) = $object_scrip->SetSortOrder( $new{$stage}{$id} );
+                                    if ( !$ret ) {
+                                        RT->Logger->error( "Couldn't update SortOrder of ObjectScrip #"
+                                                . $object_scrip->id
+                                                . ": $msg" );
+                                    }
+                                }
+                            }
+                            else {
+                                my ( $ret, $msg ) = $object_scrip->Delete;
+                                if ( !$ret ) {
+                                    RT->Logger->error( "Couldn't delete ObjectScrip #" . $object_scrip->id . ": $msg" );
+                                }
+                            }
+                        }
+                    }
+                }
+
+                for my $stage ( sort keys %new ) {
+                    for my $id ( sort { $new{$stage}{$a} <=> $new{$stage}{$b} } keys %{ $new{$stage} } ) {
+                        next if defined $current{$stage}{$id};
+
+                        my $object_scrip = RT::ObjectScrip->new( RT->SystemUser );
+                        $object_scrip->LoadByCols( Scrip => $object->id, ObjectId => $id, Stage => $stage );
+                        if ( !$object_scrip->id ) {
+                            my ( $ret, $msg ) = $object_scrip->Create(
+                                Scrip     => $object->id,
+                                ObjectId  => $id,
+                                Stage     => $stage,
+                                SortOrder => $new{$stage}{SortOrder},
+                            );
+                            if ( !$ret ) {
+                                RT->Logger->error( "Couldn't create ObjectScrip for Scrip #"
+                                        . $object->id
+                                        . " and Queue #$id: $msg" );
+                            }
+                        }
+                    }
+                }
+                next;
+            }
+        }
 
         next unless $object->can( $field ) || $object->_Accessible( $field, 'read' );
         my $old_value = $object->can( $field ) ? $object->$field : $object->_Value( $field );

commit e963da9bb05797a70d53cb12da50a5fc186645eb
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Jul 28 01:32:29 2022 +0800

    Calculate ObjectCustomField changes based on source RT if possible
    
    Previously we always calculated changes based on target RT, which could
    involve unexpected changes. E.g. if a custom field is applied to a new
    Queue in source RT and we want to sync this change to target RT,
    previously if target RT had some extra queues applied(i.e. ones not
    applied in source RT), these queues would be revoked after sync.
    
    This is initially to sync changes of specific queues, just like the
    example above.

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index 4be4295d89..2a7977c46b 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2532,18 +2532,36 @@ sub _UpdateObject {
                 my %current;
                 my %new;
 
-                my $ocfs = RT::ObjectCustomFields->new(RT->SystemUser);
-                $ocfs->LimitToCustomField($object->id);
-
-                while ( my $ocf = $ocfs->Next ) {
-                    if ( $ocf->ObjectId == 0 ) {
-                        $current{0} = 1;
-                    }
-                    else {
-                        my $added = $object->RecordClassFromLookupType->new( RT->SystemUser );
-                        $current{$ocf->ObjectId} = 1;
+                # Calculate changes based on $original if possible
+                if ( defined $original->{ApplyTo} ) {
+                    for my $item ( @{$original->{ApplyTo}} ) {
+                        # Globally applied
+                        if ( $item eq 0 ) {
+                            $current{0} = 1;
+                        }
+                        else {
+                            my $added = $object->RecordClassFromLookupType->new( RT->SystemUser );
+                            $added->Load($item);
+                            if ( $added->id ) {
+                                $current{ $added->id } = 1;
+                            }
+                        }
                     }
                 }
+                else {
+                    my $ocfs = RT::ObjectCustomFields->new(RT->SystemUser);
+                    $ocfs->LimitToCustomField($object->id);
+
+                    while ( my $ocf = $ocfs->Next ) {
+                        if ( $ocf->ObjectId == 0 ) {
+                            $current{0} = 1;
+                        }
+                        else {
+                            $current{$ocf->ObjectId} = 1;
+                        }
+                    }      
+                }
+
 
                 for my $item ( @{ $value || [] } ) {
                     if ( $item eq 0 ) {

commit 74f0e2387a71112ac83681e84c872d08e5f2c89b
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Fri Oct 1 17:52:58 2021 +0800

    Do not keep track of ObjectScrips ids when calculating changes
    
    This is like how we deleted ids of CustomFieldValues and
    ObjectCustomFieldValues, which are allowed to be different.

diff --git a/sbin/rt-dump-initialdata.in b/sbin/rt-dump-initialdata.in
index 8e5064ed9b..98a30648b2 100644
--- a/sbin/rt-dump-initialdata.in
+++ b/sbin/rt-dump-initialdata.in
@@ -285,6 +285,10 @@ sub find_differences {
         if ( $record->{Topics} ) {
             delete $_->{id} for @{$record->{Topics}};
         }
+
+        if ( $type eq 'Scrips' && $record->{Queue} ) {
+            delete $_->{id} for @{$record->{Queue}};
+        }
     }
 
     my @changes;

commit 629ca7518ed7cc7f1b42522dd0495237388f5a0e
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Sep 21 21:55:32 2021 +0800

    ACL in initialdata is applied globally by default
    
    When ObjectType/ObjectId are absent in ACL records, it means global.
    This is to get rid of errors from RT::ACE::_ParseObjectArg:
    
        Method called with wrong args

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index b51058233b..4be4295d89 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2769,6 +2769,8 @@ sub _LoadObject {
         $object->LoadByValues(
             PrincipalId   => $principal_id,
             PrincipalType => $principal_type,
+            ObjectType    => 'RT::System',
+            ObjectId      => RT->System->Id,
             map { $_ => $values->{_Original}{$_} } grep { $values->{_Original}{$_} } qw/ObjectType ObjectId RightName/,
         );
     }

commit 9fd1a70caf14fc3f6ddc4e955fdcc47a56210301
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Sep 21 21:47:18 2021 +0800

    Handle system internal and role groups for ACL updates in initialdata

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index 709c4df143..b51058233b 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2724,6 +2724,43 @@ sub _LoadObject {
                 return;
             }
         }
+        elsif ( $values->{_Original}{GroupType} ) {
+
+            my $group = RT::Group->new(RT->SystemUser);
+            if ( $values->{_Original}{'GroupDomain'} eq 'SystemInternal' ) {
+                $group->LoadSystemInternalGroup( $values->{_Original}{GroupType} );
+            }
+            elsif ( $values->{_Original}{'GroupDomain'} eq 'RT::System-Role' ) {
+                $group->LoadRoleGroup( Object => RT->System, Name => $values->{_Original}{GroupType} );
+            }
+            elsif ( $values->{_Original}{'GroupDomain'} =~ /-Role$/ ) {
+                my $object;
+                if ( $values->{_Original}{ObjectType} and $values->{_Original}{ObjectId} ) {
+                    $object = $values->{_Original}{ObjectType}->new( RT->SystemUser );
+                    my ( $ok, $msg ) = $object->Load( $values->{_Original}{ObjectId} );
+                    unless ($ok) {
+                        RT->Logger->error( "Unable to load "
+                                . $values->{_Original}{ObjectType} . " "
+                                . $values->{_Original}{ObjectId}
+                                . ": $msg" );
+                        return;
+                    }
+                }
+                else {
+                    $object = RT->System;
+                }
+                $group->LoadRoleGroup( Object => $object, Name => $values->{_Original}{GroupType} );
+            }
+
+            if ( $group->id ) {
+                $principal_id   = $group->PrincipalId;
+                $principal_type = 'Group';
+            }
+            else {
+                RT->Logger->error("Couldn't load group $values->{_Original}{GroupType}");
+                return;
+            }
+        }
         else {
             RT->Logger->error( "Invalid principal type in $class" );
             return;

commit c4ce6b83a55dae8a6cd9f7436c754de2891a27ab
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Sep 21 21:44:26 2021 +0800

    Fix typo, this block is to check GroupId

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index f557832669..709c4df143 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2720,7 +2720,7 @@ sub _LoadObject {
                 $principal_type = 'Group';
             }
             else {
-                RT->Logger->error( "Couldn't load group $values->{_Original}{UserId}" );
+                RT->Logger->error( "Couldn't load group $values->{_Original}{GroupId}" );
                 return;
             }
         }

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


hooks/post-receive
-- 
rt


More information about the rt-commit mailing list