[Rt-commit] rt branch, 4.2/migrator-fixes, created. rt-4.2.12-122-gcd06315

Shawn Moore shawn at bestpractical.com
Sun May 1 16:15:37 EDT 2016


The branch, 4.2/migrator-fixes has been created
        at  cd06315e3d98c6ee1094afa22776d5296c9c8f96 (commit)

- Log -----------------------------------------------------------------
commit 50dea853e373a5c3d3fafe3f201d8b726fda1099
Author: Dustin Graves <dustin at bestpractical.com>
Date:   Wed Feb 17 20:16:50 2016 +0000

    add AddRight calls to 4.0.1 upgrade step to prevent error message due to failure to canonicalize rights
    
    Fixes: I#31721

diff --git a/etc/upgrade/4.0.1/content b/etc/upgrade/4.0.1/content
index cc3b5f1..851c502 100644
--- a/etc/upgrade/4.0.1/content
+++ b/etc/upgrade/4.0.1/content
@@ -50,6 +50,11 @@ our @Initial = (
     sub {
         RT->Logger->debug('Removing all Delegate and PersonalGroup rights');
 
+        # this temporarily tells the system that the rights exist so it can properly canonicalize them
+        RT::System->AddRight(Admin => AdminOwnPersonalGroups  => 'Add right for 4.0.1 upgrade steps');
+        RT::System->AddRight(Admin => AdminAllPersonalGroups  => 'Add right for 4.0.1 upgrade steps');
+        RT::System->AddRight(Admin => DelegateRights          => 'Add right for 4.0.1 upgrade steps');
+
         my $acl = RT::ACL->new(RT->SystemUser);
         for my $right (qw/AdminOwnPersonalGroups AdminAllPersonalGroups DelegateRights/) {
             $acl->Limit( FIELD => 'RightName', VALUE => $right );

commit 3b8ae5b6697ffd26734c9db46b30002af9460193
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Wed Feb 17 23:19:49 2016 +0000

    Update detection of find version in configure
    
    Commit 4028723 added code to detect the version of find
    because Debian is changing to a newer version of GNU
    find with different syntax than the traditional find.
    However, the change in that commit no longer worked on
    Mac OS X, a BSD-type system.
    
    Modify the find check to use the --version option supported
    on GNU and use GNU syntax if found. Otherwise default to the
    previous BSD syntax which was the previous setting for all
    systems.

diff --git a/configure.ac b/configure.ac
index 44f5384..c671138 100755
--- a/configure.ac
+++ b/configure.ac
@@ -36,21 +36,13 @@ fi
 
 dnl BSD find uses -perm +xxxx, GNU find has deprecated this syntax in favour of
 dnl -perm /xxx.
-AC_MSG_CHECKING([whether find supports -perm /x or find -perm +x])
-if find -perm /0100 -not -perm /0100
-then
-	FINDPERM="/"
-elif
-	find -perm +0100 -not -perm +0100
-then
-	FINDPERM="+"
-else
-	FINDPERM="na"
-fi
-AC_MSG_RESULT([${FINDPERM}])
-if test "x$FINDPERM" = "xna" ; then
-	AC_MSG_WARN([local find program supports neither -perm /0111 nor -perm +0111, make fixperms will not work])
-fi
+AC_MSG_CHECKING([checking version of find])
+AS_IF([find --version 2>&1 | grep 'GNU'],
+      [   FINDPERM="/"
+          AC_MSG_RESULT([configuring for GNU find]) ],
+      [   FINDPERM="+"
+          AC_MSG_RESULT([configuring for BSD find]) ])
+
 AC_SUBST([FINDPERM])
 
 dnl WEB_HANDLER

commit 1cac7d3a64b1b3513610069f7b3895e359585475
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Sat Mar 12 16:27:00 2016 +0000

    Serialize ObjectClass ObjectId using indirection
    
        Otherwise article classes get applied to the wrong queue (the
        one which happens to have had the same ID as one in the database
        being serialized). This method is mostly lifted from
        ObjectCustomField.
    
    Fixes: I#31804

diff --git a/lib/RT/ObjectClass.pm b/lib/RT/ObjectClass.pm
index c51d9d4..f5a5da6 100644
--- a/lib/RT/ObjectClass.pm
+++ b/lib/RT/ObjectClass.pm
@@ -229,6 +229,18 @@ sub FindDependencies {
     $deps->Add( out => $obj );
 }
 
+sub Serialize {
+    my $self = shift;
+    my %args = (@_);
+    my %store = $self->SUPER::Serialize(@_);
+
+    if ($store{ObjectId}) {
+        my $obj = $self->ObjectType->new( RT->SystemUser );
+        $obj->Load( $store{ObjectId} );
+        $store{ObjectId} = \($obj->UID);
+    }
+    return %store;
+}
 
 RT::Base->_ImportOverlays();
 

commit 06c4c4393d1ce6223aa280f152c5da14bfb6a2fd
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Sat Mar 12 16:43:53 2016 +0000

    Serialize ObjectScrip ObjectId using indirection
    
        Otherwise scrip gets applied to the wrong queue (the one which
        happens to have had the same ID as one in the database being
        serialized). This method is mostly lifted from
        ObjectCustomField.
    
    Fixes: I#31805

diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
index 8399398..b270d25 100644
--- a/lib/RT/ObjectScrip.pm
+++ b/lib/RT/ObjectScrip.pm
@@ -272,6 +272,19 @@ sub FindDependencies {
     }
 }
 
+sub Serialize {
+    my $self = shift;
+    my %args = (@_);
+    my %store = $self->SUPER::Serialize(@_);
+
+    if ($store{ObjectId}) {
+        my $obj = RT::Queue->new( RT->SystemUser );
+        $obj->Load( $store{ObjectId} );
+        $store{ObjectId} = \($obj->UID);
+    }
+    return %store;
+}
+
 RT::Base->_ImportOverlays();
 
 1;

commit 152c90c600c19cf470e9202b21b1cbb82a100072
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Sat Mar 12 16:51:44 2016 +0000

    Serialize ObjectTopic ObjectId using indirection
    
        Otherwise a topic gets applied to the wrong article (the one
        which happens to have had the same ID as one in the database
        being serialized). This method is mostly lifted from
        ObjectCustomField.
    
    Fixes: I#31803

diff --git a/lib/RT/ObjectTopic.pm b/lib/RT/ObjectTopic.pm
index c9429fd..dfe2bfd 100644
--- a/lib/RT/ObjectTopic.pm
+++ b/lib/RT/ObjectTopic.pm
@@ -212,6 +212,19 @@ sub FindDependencies {
     $deps->Add( out => $obj );
 }
 
+sub Serialize {
+    my $self = shift;
+    my %args = (@_);
+    my %store = $self->SUPER::Serialize(@_);
+
+    if ($store{ObjectId}) {
+        my $obj = $self->ObjectType->new( RT->SystemUser );
+        $obj->Load( $store{ObjectId} );
+        $store{ObjectId} = \($obj->UID);
+    }
+    return %store;
+}
+
 RT::Base->_ImportOverlays();
 
 1;

commit 84b02228763304a20edec35cc8547aa9e9c99862
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Sat Mar 12 18:03:31 2016 +0000

    Avoid breaking rights granted to users when using rt-importer
    
        The "user rights" admin pages issue a SQL query where each
        user's ACLEquivalence group name is expected to be "UserEquiv".
        However, before this commit, the importer system changed each
        group's name from the expected "UserEquiv" to be e.g. "User 99",
        since the user ID changes during import. So because the group
        name was no longer "UserEquiv", the admin UI was failing to find
        the existant but malformed ACL entries.
    
        ACLEquivalence UserEquiv groups stopped using the user's ID in
        their name as of 71fcde32, which is exactly when this regression
        was introduced. Previously, user rights importing worked because
        both the migrator and the ACL subsytems correctly used the Type
        of the group, which was "UserEquiv". (The regression was
        introduced because ACLs started using Name in place of Type)
    
    Fixes: I#31806

diff --git a/lib/RT/Group.pm b/lib/RT/Group.pm
index c412ba6..81ac325 100644
--- a/lib/RT/Group.pm
+++ b/lib/RT/Group.pm
@@ -1741,13 +1741,12 @@ sub PreInflate {
         return;
     };
 
-    # Go looking for the pre-existing version of the it
+    # Go looking for the pre-existing version of it
     if ($data->{Domain} eq "ACLEquivalence") {
         $obj->LoadACLEquivalenceGroup( $data->{Instance} );
         return $duplicated->() if $obj->Id;
 
-        # Update the name and description for the new ID
-        $data->{Name} = 'User '. $data->{Instance};
+        # Update description for the new ID
         $data->{Description} = 'ACL equiv. for user '.$data->{Instance};
     } elsif ($data->{Domain} eq "UserDefined") {
         $data->{Name} = $importer->Qualify($data->{Name});

commit 5c2cdaccdac956404b013dcd5b287ad48c929946
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Mon Mar 21 19:37:23 2016 +0000

    Serialize dashboards in menu preference using indirection
    
        Otherwise the old dashboard IDs will be kept in the user's
        preferences, causing the menu to (in most cases) be empty.
    
    Fixes: I#31810

diff --git a/lib/RT/Attribute.pm b/lib/RT/Attribute.pm
index 9943b57..9f69179 100644
--- a/lib/RT/Attribute.pm
+++ b/lib/RT/Attribute.pm
@@ -632,6 +632,18 @@ sub FindDependencies {
 
     $self->SUPER::FindDependencies($walker, $deps);
     $deps->Add( out => $self->Object );
+
+    # dashboards in menu attribute has dependencies on each of its dashboards
+    if ($self->Name eq RT::User::_PrefName("DashboardsInMenu")) {
+        my $content = $self->Content;
+        for my $pane (values %{ $content || {} }) {
+            for my $dash_id (@$pane) {
+                my $attr = RT::Attribute->new($self->CurrentUser);
+                $attr->LoadById($dash_id);
+                $deps->Add( out => $attr );
+            }
+        }
+    }
 }
 
 sub PreInflate {
@@ -642,9 +654,40 @@ sub PreInflate {
         my $on_uid = ${ $data->{Object} };
         return if $importer->ShouldSkipTransaction($on_uid);
     }
+
+    # decode UIDs to be raw dashboard IDs
+    if ($data->{Name} eq RT::User::_PrefName("DashboardsInMenu")) {
+        my $content = $class->_DeserializeContent($data->{Content});
+        for my $pane (values %{ $content || {} }) {
+            @$pane = map { $importer->LookupObj($$_)->Id } @$pane;
+        }
+        $data->{Content} = $class->_SerializeContent($content);
+    }
+
     return $class->SUPER::PreInflate( $importer, $uid, $data );
 }
 
+sub Serialize {
+    my $self = shift;
+    my %args = (@_);
+    my %store = $self->SUPER::Serialize(@_);
+
+    # encode raw dashboard IDs to be UIDs
+    if ($store{Name} eq RT::User::_PrefName("DashboardsInMenu")) {
+        my $content = $self->_DeserializeContent($store{Content});
+        for my $pane (values %{ $content || {} }) {
+            for (@$pane) {
+                my $attr = RT::Attribute->new($self->CurrentUser);
+                $attr->LoadById($_);
+                $_ = \($attr->UID);
+            }
+        }
+        $store{Content} = $self->_SerializeContent($content);
+    }
+
+    return %store;
+}
+
 RT::Base->_ImportOverlays();
 
 1;

commit 69ab5435b21ce782d6114ec985a57ff00ccca29f
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Mon Mar 21 23:11:30 2016 +0000

    Add a way to register a postpone callback in importer
    
        This is meant for inflating saved searches and dashboards (see followup commits)

diff --git a/lib/RT/Migrate/Importer.pm b/lib/RT/Migrate/Importer.pm
index 7897434..0e6456a 100644
--- a/lib/RT/Migrate/Importer.pm
+++ b/lib/RT/Migrate/Importer.pm
@@ -179,6 +179,9 @@ sub Resolve {
             Field => $ref->{uri},
             Value => $self->LookupObj($uid)->URI,
         ) if defined $ref->{uri};
+        if (my $method = $ref->{method}) {
+            $obj->$method($self, $ref, $class, $id);
+        }
     }
     delete $self->{Pending}{$uid};
 }
@@ -332,7 +335,7 @@ sub Create {
     # Load it back to get real values into the columns
     $obj = $class->new( RT->SystemUser );
     $obj->Load( $id );
-    $obj->PostInflate( $self );
+    $obj->PostInflate( $self, $uid );
 
     return $obj;
 }

commit 97cf2096df4b088dbc26de4db0b964e5d16219fd
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Mon Mar 21 23:12:52 2016 +0000

    Don't skip importing RT->System searches
    
        While this may duplicate the three builtin searches (bookmarked,
        unowned, my tickets), we definitely do not want to lose any user-defined
        searches saved at the system level.
    
        Furthermore, when merging two RT instances, we may want each user to
        continue using their system's customization of those three builtin
        searches.

diff --git a/lib/RT/Attribute.pm b/lib/RT/Attribute.pm
index 9f69179..c493441 100644
--- a/lib/RT/Attribute.pm
+++ b/lib/RT/Attribute.pm
@@ -652,7 +652,12 @@ sub PreInflate {
 
     if ($data->{Object} and ref $data->{Object}) {
         my $on_uid = ${ $data->{Object} };
-        return if $importer->ShouldSkipTransaction($on_uid);
+
+        # skip attributes of objects we're not inflating
+        # exception: we don't inflate RT->System, but we want RT->System's searches
+        unless ($on_uid eq RT->System->UID && $data->{Name} =~ /Search/) {
+            return if $importer->ShouldSkipTransaction($on_uid);
+        }
     }
 
     # decode UIDs to be raw dashboard IDs

commit f464c3f2fce0a81a77f0efe54a9311a2fc240b39
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Mon Mar 21 23:23:07 2016 +0000

    Serialize RT at a Glance preferences using indirection
    
        Otherwise the old search IDs will be kept in the user's preferences,
        causing the homepage to render with "search not found" type errors.
    
    Fixes: I#31809

diff --git a/lib/RT/Attribute.pm b/lib/RT/Attribute.pm
index c493441..9cf42f6 100644
--- a/lib/RT/Attribute.pm
+++ b/lib/RT/Attribute.pm
@@ -644,6 +644,33 @@ sub FindDependencies {
             }
         }
     }
+    # homepage settings attribute has dependencies on each of the searches in it
+    elsif ($self->Name eq RT::User::_PrefName("HomepageSettings")) {
+        my $content = $self->Content;
+        for my $pane (values %{ $content || {} }) {
+            for my $component (@$pane) {
+                # this hairy code mirrors what's in the saved search loader
+                # in /Elements/ShowSearch
+                if ($component->{type} eq 'saved') {
+                    if ($component->{name} =~ /^(.*?)-(\d+)-SavedSearch-(\d+)$/) {
+                        my $attr = RT::Attribute->new($self->CurrentUser);
+                        $attr->LoadById($3);
+                        $deps->Add( out => $attr );
+                    }
+                }
+                elsif ($component->{type} eq 'system') {
+                    my ($search) = RT::System->new($self->CurrentUser)->Attributes->Named( 'Search - ' . $component->{name} );
+                    unless ( $search && $search->Id ) {
+                        my (@custom_searches) = RT::System->new($self->CurrentUser)->Attributes->Named('SavedSearch');
+                        foreach my $custom (@custom_searches) {
+                            if ($custom->Description eq $component->{name}) { $search = $custom; last }
+                        }
+                    }
+                    $deps->Add( out => $search ) if $search;
+                }
+            }
+        }
+    }
 }
 
 sub PreInflate {
@@ -660,16 +687,89 @@ sub PreInflate {
         }
     }
 
+    return $class->SUPER::PreInflate( $importer, $uid, $data );
+}
+
+# this method will be called repeatedly to fix up this attribute's contents
+# (a list of searches, dashboards) during the import process, as the
+# ordinary dependency resolution system can't quite handle the subtlety
+# involved (e.g. a user simply declares out-dependencies on all of her
+# attributes, but those attributes (e.g. dashboards, saved searches,
+# dashboards in menu preferences) have dependencies amongst themselves).
+# if this attribute (e.g. a user's dashboard) fails to load an attribute
+# (e.g. a user's saved search) then it postpones and repeats the postinflate
+# process again when that user's saved search has been imported
+# this method updates Content each time through, each time getting closer and
+# closer to the fully inflated attribute
+sub PostInflateFixup {
+    my $self     = shift;
+    my $importer = shift;
+    my $spec     = shift;
+
     # decode UIDs to be raw dashboard IDs
-    if ($data->{Name} eq RT::User::_PrefName("DashboardsInMenu")) {
-        my $content = $class->_DeserializeContent($data->{Content});
+    if ($self->Name eq RT::User::_PrefName("DashboardsInMenu")) {
+        my $content = $self->Content;
+
         for my $pane (values %{ $content || {} }) {
-            @$pane = map { $importer->LookupObj($$_)->Id } @$pane;
+            for (@$pane) {
+                if (ref($_) eq 'SCALAR') {
+                    my $attr = $importer->LookupObj($$_);
+                    if ($attr) {
+                        $_ = $attr->Id;
+                    }
+                    else {
+                        $importer->Postpone(
+                            for    => $$_,
+                            uid    => $spec->{uid},
+                            method => 'PostInflateFixup',
+                        );
+                    }
+                }
+            }
         }
-        $data->{Content} = $class->_SerializeContent($content);
+        $self->SetContent($content);
     }
+    # decode UIDs to be saved searches
+    elsif ($self->Name eq RT::User::_PrefName("HomepageSettings")) {
+        my $content = $self->Content;
 
-    return $class->SUPER::PreInflate( $importer, $uid, $data );
+        for my $pane (values %{ $content || {} }) {
+            for (@$pane) {
+                if (ref($_->{uid}) eq 'SCALAR') {
+                    my $uid = $_->{uid};
+                    my $attr = $importer->LookupObj($$uid);
+
+                    if ($attr) {
+                        if ($_->{type} eq 'saved') {
+                            $_->{name} = join '-', $attr->ObjectType, $attr->ObjectId, 'SavedSearch', $attr->id;
+                        }
+                        # if type is system, name doesn't need to change
+                        # if type is anything else, pass it through as is
+                        delete $_->{uid};
+                    }
+                    else {
+                        $importer->Postpone(
+                            for    => $$uid,
+                            uid    => $spec->{uid},
+                            method => 'PostInflateFixup',
+                        );
+                    }
+                }
+            }
+        }
+        $self->SetContent($content);
+    }
+}
+
+sub PostInflate {
+    my $self = shift;
+    my ($importer, $uid) = @_;
+
+    $self->SUPER::PostInflate( $importer, $uid );
+
+    # this method is separate because it needs to be callable multple times,
+    # and we can't guarantee that SUPER::PostInflate can deal with that
+    $self->PostInflateFixup($importer, { uid => $uid });
 }
 
 sub Serialize {
@@ -689,6 +789,39 @@ sub Serialize {
         }
         $store{Content} = $self->_SerializeContent($content);
     }
+    # encode saved searches to be UIDs
+    elsif ($store{Name} eq RT::User::_PrefName("HomepageSettings")) {
+        my $content = $self->_DeserializeContent($store{Content});
+        for my $pane (values %{ $content || {} }) {
+            for (@$pane) {
+                # this hairy code mirrors what's in the saved search loader
+                # in /Elements/ShowSearch
+                if ($_->{type} eq 'saved') {
+                    if ($_->{name} =~ /^(.*?)-(\d+)-SavedSearch-(\d+)$/) {
+                        my $attr = RT::Attribute->new($self->CurrentUser);
+                        $attr->LoadById($3);
+                        $_->{uid} = \($attr->UID);
+                    }
+                    # if we can't parse the name, just pass it through
+                }
+                elsif ($_->{type} eq 'system') {
+                    my ($search) = RT::System->new($self->CurrentUser)->Attributes->Named( 'Search - ' . $_->{name} );
+                    unless ( $search && $search->Id ) {
+                        my (@custom_searches) = RT::System->new($self->CurrentUser)->Attributes->Named('SavedSearch');
+                        foreach my $custom (@custom_searches) {
+                            if ($custom->Description eq $_->{name}) { $search = $custom; last }
+                        }
+                    }
+                    # if we can't load the search, just pass it through
+                    if ($search) {
+                        $_->{uid} = \($search->UID);
+                    }
+                }
+                # pass through everything else (e.g. component)
+            }
+        }
+        $store{Content} = $self->_SerializeContent($content);
+    }
 
     return %store;
 }

commit f05b6682d00d0113225dbcc62f79b8be0c8aa7e9
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Mon Mar 21 23:42:27 2016 +0000

    Serialize dashboards using indirection
    
        Otherwise the old search and dashboard IDs will be kept in the component
        list, causing the dashboard to render with "search not found" type errors.
    
    Fixes: I#31808

diff --git a/lib/RT/Attribute.pm b/lib/RT/Attribute.pm
index 9cf42f6..74c6be9 100644
--- a/lib/RT/Attribute.pm
+++ b/lib/RT/Attribute.pm
@@ -671,6 +671,19 @@ sub FindDependencies {
             }
         }
     }
+    # dashboards have dependencies on all the searches and dashboards they use
+    elsif ($self->Name eq 'Dashboard') {
+        my $content = $self->Content;
+        for my $pane (values %{ $content->{Panes} || {} }) {
+            for my $component (@$pane) {
+                if ($component->{portlet_type} eq 'search' || $component->{portlet_type} eq 'dashboard') {
+                    my $attr = RT::Attribute->new($self->CurrentUser);
+                    $attr->LoadById($component->{id});
+                    $deps->Add( out => $attr );
+                }
+            }
+        }
+    }
 }
 
 sub PreInflate {
@@ -759,6 +772,33 @@ sub PostInflateFixup {
         }
         $self->SetContent($content);
     }
+    elsif ($self->Name eq 'Dashboard') {
+        my $content = $self->Content;
+
+        for my $pane (values %{ $content->{Panes} || {} }) {
+            for (@$pane) {
+                if (ref($_->{uid}) eq 'SCALAR') {
+                    my $uid = $_->{uid};
+                    my $attr = $importer->LookupObj($$uid);
+
+                    if ($attr) {
+                        # update with the new id numbers assigned to us
+                        $_->{id} = $attr->Id;
+                        $_->{privacy} = join '-', $attr->ObjectType, $attr->ObjectId;
+                        delete $_->{uid};
+                    }
+                    else {
+                        $importer->Postpone(
+                            for    => $$uid,
+                            uid    => $spec->{uid},
+                            method => 'PostInflateFixup',
+                        );
+                    }
+                }
+            }
+        }
+        $self->SetContent($content);
+    }
 }
 
 sub PostInflate {
@@ -822,6 +862,21 @@ sub Serialize {
         }
         $store{Content} = $self->_SerializeContent($content);
     }
+    # encode saved searches and dashboards to be UIDs
+    elsif ($store{Name} eq 'Dashboard') {
+        my $content = $self->_DeserializeContent($store{Content}) || {};
+        for my $pane (values %{ $content->{Panes} || {} }) {
+            for (@$pane) {
+                if ($_->{portlet_type} eq 'search' || $_->{portlet_type} eq 'dashboard') {
+                    my $attr = RT::Attribute->new($self->CurrentUser);
+                    $attr->LoadById($_->{id});
+                    $_->{uid} = \($attr->UID);
+                }
+                # pass through everything else (e.g. component)
+            }
+        }
+        $store{Content} = $self->_SerializeContent($content);
+    }
 
     return %store;
 }

commit 1240499ed3e548486b2a857447c8db990a09a987
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Mon Mar 21 23:43:03 2016 +0000

    Serialize dashboard subscriptions using indirection
    
        Otherwise the old dashboard ID will be kept in the subscription,
        causing it to go missing.

diff --git a/lib/RT/Attribute.pm b/lib/RT/Attribute.pm
index 74c6be9..a78a6f6 100644
--- a/lib/RT/Attribute.pm
+++ b/lib/RT/Attribute.pm
@@ -684,6 +684,13 @@ sub FindDependencies {
             }
         }
     }
+    # subscriptions have dependencies their dashboard
+    elsif ($self->Name eq 'Subscription') {
+        my $content = $self->Content;
+        my $attr = RT::Attribute->new($self->CurrentUser);
+        $attr->LoadById($content->{DashboardId});
+        $deps->Add( out => $attr );
+    }
 }
 
 sub PreInflate {
@@ -799,6 +806,23 @@ sub PostInflateFixup {
         }
         $self->SetContent($content);
     }
+    elsif ($self->Name eq 'Subscription') {
+        my $content = $self->Content;
+        if (ref($content->{DashboardId}) eq 'SCALAR') {
+            my $attr = $importer->LookupObj(${ $content->{DashboardId} });
+            if ($attr) {
+                $content->{DashboardId} = $attr->Id;
+            }
+            else {
+                $importer->Postpone(
+                    for    => ${ $content->{DashboardId} },
+                    uid    => $spec->{uid},
+                    method => 'PostInflateFixup',
+                );
+            }
+        }
+        $self->SetContent($content);
+    }
 }
 
 sub PostInflate {
@@ -877,6 +901,14 @@ sub Serialize {
         }
         $store{Content} = $self->_SerializeContent($content);
     }
+    # encode subscriptions to have dashboard UID
+    elsif ($store{Name} eq 'Subscription') {
+        my $content = $self->_DeserializeContent($store{Content});
+        my $attr = RT::Attribute->new($self->CurrentUser);
+        $attr->LoadById($content->{DashboardId});
+        $content->{DashboardId} = \($attr->UID);
+        $store{Content} = $self->_SerializeContent($content);
+    }
 
     return %store;
 }

commit cd06315e3d98c6ee1094afa22776d5296c9c8f96
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Fri Mar 25 01:29:47 2016 +0000

    --exclude-organization option for rt-importer
    
        When records are already known to be unique this is unwanted noise.
    
    Fixes: I#31813
    Fixes: I#31812

diff --git a/lib/RT/Migrate/Importer.pm b/lib/RT/Migrate/Importer.pm
index 0e6456a..5c25d9a 100644
--- a/lib/RT/Migrate/Importer.pm
+++ b/lib/RT/Migrate/Importer.pm
@@ -65,17 +65,20 @@ sub new {
 sub Init {
     my $self = shift;
     my %args = (
-        OriginalId  => undef,
-        Progress    => undef,
-        Statefile   => undef,
-        DumpObjects => undef,
-        HandleError => undef,
+        OriginalId          => undef,
+        Progress            => undef,
+        Statefile           => undef,
+        DumpObjects         => undef,
+        HandleError         => undef,
+        ExcludeOrganization => undef,
         @_,
     );
 
     # Should we attempt to preserve record IDs as they are created?
     $self->{OriginalId} = $args{OriginalId};
 
+    $self->{ExcludeOrganization} = $args{ExcludeOrganization};
+
     $self->{Progress} = $args{Progress};
 
     $self->{HandleError} = sub { 0 };
@@ -294,6 +297,7 @@ sub Qualify {
     my ($string) = @_;
     return $string if $self->{Clone};
     return $string if not defined $self->{Organization};
+    return $string if $self->{ExcludeOrganization};
     return $string if $self->{Organization} eq $RT::Organization;
     return $self->{Organization}.": $string";
 }
@@ -402,9 +406,13 @@ sub ReadStream {
     # If it's a ticket, we might need to create a
     # TicketCustomField for the previous ID
     if ($class eq "RT::Ticket" and $self->{OriginalId}) {
+        my $value = $self->{ExcludeOrganization}
+                  ? $origid
+                  : $self->Organization . ":$origid";
+
         my ($id, $msg) = $obj->AddCustomFieldValue(
             Field             => $self->{OriginalId},
-            Value             => $self->Organization . ":$origid",
+            Value             => $value,
             RecordTransaction => 0,
         );
         warn "Failed to add custom field to $uid: $msg"
diff --git a/lib/RT/Migrate/Importer/File.pm b/lib/RT/Migrate/Importer/File.pm
index cfad9ae..3d36f3d 100644
--- a/lib/RT/Migrate/Importer/File.pm
+++ b/lib/RT/Migrate/Importer/File.pm
@@ -192,7 +192,7 @@ sub SaveState {
            NewQueues NewCFs
            SkipTransactions Pending Invalid
            UIDs
-           OriginalId Clone
+           OriginalId ExcludeOrganization Clone
           /;
     Storable::nstore(\%data, $self->{Statefile});
 
diff --git a/sbin/rt-importer.in b/sbin/rt-importer.in
index 6d3cda5..1f9e6e6 100644
--- a/sbin/rt-importer.in
+++ b/sbin/rt-importer.in
@@ -95,6 +95,7 @@ GetOptions(
 
     "resume!",
     "originalid|i=s",
+    "exclude-organization",
 
     "ask",
     "ignore-errors",
@@ -142,11 +143,12 @@ elsif ($OPT{'ignore-errors'}) {
 }
 
 my $import = RT::Migrate::Importer::File->new(
-    Directory   => $dir,
-    OriginalId  => $OPT{originalid},
-    DumpObjects => $OPT{dump},
-    Resume      => $OPT{resume},
-    HandleError => $error_handler,
+    Directory           => $dir,
+    OriginalId          => $OPT{originalid},
+    ExcludeOrganization => $OPT{'exclude-organization'},
+    DumpObjects         => $OPT{dump},
+    Resume              => $OPT{resume},
+    HandleError         => $error_handler,
 );
 
 if ($import->Metadata and -t STDOUT and not $OPT{quiet}) {
@@ -236,6 +238,12 @@ Places the original ticket organization and ID into a global custom
 field with the given name.  If no global ticket custom field with that
 name is found in the current database, it will create one.
 
+=item B<--exclude-organization>
+
+Ordinarily certain records (groups, queues, the B<--originalid> custom field)
+include the organization name of the original RT instance. Use this option to
+suppress that behavior and use the original name directly.
+
 =item B<--ask>
 
 Prompt for action when an error occurs inserting a record into the

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


More information about the rt-commit mailing list