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

BPS Git Server git at git.bestpractical.com
Wed Feb 1 21:17:22 UTC 2023


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  0578f0b757b26255b237a14a9be66f651338929e (commit)

- Log -----------------------------------------------------------------
commit 0578f0b757b26255b237a14a9be66f651338929e
Author: Jason Crome <jcrome at bestpractical.com>
Date:   Wed Feb 1 16:14:22 2023 -0500

    Don't export attributes that may contain JSON
    
    Certain attributes associated with queues may contain JSON, which causes
    the serializer to die when it encounters them. Don't attempt to dump and
    serialize these, therefore avoiding this problem altogether.

diff --git a/lib/RT/Migrate/Serializer/JSON.pm b/lib/RT/Migrate/Serializer/JSON.pm
index 3f03a70f2f..2a64c8f364 100644
--- a/lib/RT/Migrate/Serializer/JSON.pm
+++ b/lib/RT/Migrate/Serializer/JSON.pm
@@ -576,7 +576,7 @@ sub CanonicalizeAttributes {
     for my $key ( keys %{ $self->{Records}{'RT::Attribute'} } ) {
         delete $self->{Records}{'RT::Attribute'}{$key}
           if $self->{Records}{'RT::Attribute'}{$key}{Name} =~
-          /^(?:UpgradeHistory|QueueCacheNeedsUpdate|CatalogCacheNeedsUpdate|CustomRoleCacheNeedsUpdate|RecentlyViewedTickets)$/;
+          /^(?:UpgradeHistory|QueueCacheNeedsUpdate|CatalogCacheNeedsUpdate|CustomRoleCacheNeedsUpdate|RecentlyViewedTickets|REST2Jobs|REST2JobsQueue)$/;
     }
 }
 

commit 3000abf167e7e4ea5fc05d1340ab620869b44c32
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 82a9ffb9541384682ce290d4dcd77e85e4a7d2d6
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 f74eb43702..4c14af113e 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -3185,6 +3185,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 711e647d06c1e330de2d7e02d88b91c772d60ce3
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 c53f45b49b..f74eb43702 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -1567,10 +1567,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'} );
                 }
@@ -2963,12 +2984,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) {
@@ -2982,6 +3004,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 31acd2c4cfb2f700a014ec955fcecd4a25d206a4
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/lib/RT/Migrate/Serializer.pm b/lib/RT/Migrate/Serializer.pm
index bce8530f22..1b740717f5 100644
--- a/lib/RT/Migrate/Serializer.pm
+++ b/lib/RT/Migrate/Serializer.pm
@@ -317,9 +317,12 @@ sub PushBasics {
     }
 
     if ($self->{Queues}) {
-        my $queues = RT::Queues->new(RT->SystemUser);
-        $queues->Limit(FIELD => 'id', OPERATOR => 'IN', VALUE => $self->{Queues});
-        $self->PushObj($queues);
+        # MariaDB doesn't like empty list
+        if ( @{ $self->{Queues} } ) {
+            my $queues = RT::Queues->new(RT->SystemUser);
+            $queues->Limit(FIELD => 'id', OPERATOR => 'IN', VALUE => $self->{Queues});
+            $self->PushObj($queues);
+        }
     }
     else {
         $self->PushCollections(qw(Queues));
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 55eb2256d1eb97fc632e787447349d046e77f4b5
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 238125c3bc..c53f45b49b 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2673,7 +2673,7 @@ sub _UpdateObject {
                 next;
             }
         }
-        if ( $class eq 'RT::CustomRole' ) {
+        elsif ( $class eq 'RT::CustomRole' ) {
             if ( $field eq 'ApplyTo' ) {
                 my %current;
                 my %new;
@@ -2724,6 +2724,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 617f83c2027243d4fe00e55804f5abd25811f8dc
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 a6305c4bc6..238125c3bc 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2673,6 +2673,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 be4e7a3477f6393e41d4fec96617fb03aed6d5a6
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 5916045109..a6305c4bc6 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2530,8 +2530,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 );
@@ -2669,6 +2673,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 853d52e6c6d57721751cd2499cece201dc42fc48
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 628e8f4f73..5916045109 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2545,18 +2545,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 e9519560d59da970910a32ea4c1351646cb5e9f9
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 c2ff80caf5c021c2c699a67d280d3cc50968510d
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 23f30ca930..628e8f4f73 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2782,6 +2782,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 ccf35a2bfcc18b4d29038090766e253295fdda4f
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 8f34aac767..23f30ca930 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2737,6 +2737,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 29ad13d559a683c50d35f468f83f71dfd71a6d72
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 5b63911441..8f34aac767 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -2733,7 +2733,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