[Rt-commit] rt branch, 4.2/migrator-fixes, created. rt-4.2.12-123-g37e7c98
Shawn Moore
shawn at bestpractical.com
Mon Jun 27 18:24:27 EDT 2016
The branch, 4.2/migrator-fixes has been created
at 37e7c98e37f7ad7ac6ad8983fbc3c4702211d23d (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 a8bf0f391208c8429feb66fad34dcd71f16d47a5
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 a8b281e18e04197f2ac7206129d7fba26e44ae8a
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 aea531f3f5383a53b3ef8114b501b75c7fa7e84e
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 037839bc7e05a3fcaa9bd9bf9e8efb0d06f9549b
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 1e9287ae1dd235b9426524c00cebe7898251db46
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 7d6f9d6b408efa4c767fecf3418c04e267db0acf
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 0a408adb24ada26229bea91f6b08a13258e0d2dc
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 fce4f0831afd4ea8c7f82bfed105db876407675e
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 5d01baa9207ac93e02a7078c5da777f3d7805389
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..eddd37e 100644
--- a/lib/RT/Attribute.pm
+++ b/lib/RT/Attribute.pm
@@ -684,6 +684,13 @@ sub FindDependencies {
}
}
}
+ # each subscription depends on its 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 330623dc5e3253be01227ed8053c8ad7c2a1227d
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..cc1a426 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}) {
@@ -222,6 +224,13 @@ current database; this may include users, queues, and tickets.
It is possible to stop the import process with ^C; it can be later
resumed by re-running the importer.
+Certain records (notably queues and groups) will have their original
+Organization name prepended to them on import. This is primarily to avoid
+duplicate names (for example importing a General queue into an RT that
+already has one would otherwise cause a name collision error). You may
+suppress this behavior by passing the B<--exclude-organization> flag to
+C<rt-importer>.
+
=head2 OPTIONS
=over
@@ -236,6 +245,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
commit 37e7c98e37f7ad7ac6ad8983fbc3c4702211d23d
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Sun May 1 16:17:14 2016 -0400
Serialize Transaction OldReference and NewReference using indirection
Otherwise each transaction which records a custom field update would
point to the wrong ObjectCustomFieldValue records, causing RT to
display incorrect old and new values in, especially, ticket history.
diff --git a/lib/RT/Transaction.pm b/lib/RT/Transaction.pm
index d73bf38..907d1d8 100644
--- a/lib/RT/Transaction.pm
+++ b/lib/RT/Transaction.pm
@@ -2041,6 +2041,9 @@ sub Serialize {
my $cf = RT::CustomField->new( RT->SystemUser );
$cf->Load( $store{Field} );
$store{Field} = \($cf->UID);
+
+ $store{OldReference} = \($self->OldReferenceObject->UID) if $self->OldReference;
+ $store{NewReference} = \($self->NewReferenceObject->UID) if $self->NewReference;
} elsif ($type =~ /^(Take|Untake|Force|Steal|Give)$/) {
for my $field (qw/OldValue NewValue/) {
my $user = RT::User->new( RT->SystemUser );
-----------------------------------------------------------------------
More information about the rt-commit
mailing list