rt 4.2.13 ticket#13852
[freeside.git] / rt / sbin / rt-dump-metadata.in
index f58371f..c212749 100644 (file)
@@ -3,7 +3,7 @@
 #
 # COPYRIGHT:
 #
 #
 # COPYRIGHT:
 #
-# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2016 Best Practical Solutions, LLC
 #                                          <sales@bestpractical.com>
 #
 # (Except where explicitly superseded by other copyright notices)
 #                                          <sales@bestpractical.com>
 #
 # (Except where explicitly superseded by other copyright notices)
 #
 # END BPS TAGGED BLOCK }}}
 use strict;
 #
 # END BPS TAGGED BLOCK }}}
 use strict;
+use warnings;
 
 # As we specify that XML is UTF-8 and we output it to STDOUT, we must be sure
 # it is UTF-8 so further XMLin will not break
 binmode( STDOUT, ":utf8" );
 
 # fix lib paths, some may be relative
 
 # As we specify that XML is UTF-8 and we output it to STDOUT, we must be sure
 # it is UTF-8 so further XMLin will not break
 binmode( STDOUT, ":utf8" );
 
 # fix lib paths, some may be relative
-BEGIN {
+BEGIN { # BEGIN RT CMD BOILERPLATE
     require File::Spec;
     require File::Spec;
-    my @libs = ( "@RT_LIB_PATH@", "@LOCAL_LIB_PATH@" );
+    require Cwd;
+    my @libs = ("@RT_LIB_PATH@", "@LOCAL_LIB_PATH@");
     my $bin_path;
 
     for my $lib (@libs) {
         unless ( File::Spec->file_name_is_absolute($lib) ) {
     my $bin_path;
 
     for my $lib (@libs) {
         unless ( File::Spec->file_name_is_absolute($lib) ) {
-            unless ($bin_path) {
-                if ( File::Spec->file_name_is_absolute(__FILE__) ) {
-                    $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
-                } else {
-                    require FindBin;
-                    no warnings "once";
-                    $bin_path = $FindBin::Bin;
-                }
-            }
+            $bin_path ||= ( File::Spec->splitpath(Cwd::abs_path(__FILE__)) )[1];
             $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
         }
         unshift @INC, $lib;
             $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
         }
         unshift @INC, $lib;
@@ -76,24 +70,16 @@ BEGIN {
 
 }
 
 
 }
 
-use Getopt::Long;
+use RT::Interface::CLI qw(Init);
 my %opt;
 my %opt;
-GetOptions( \%opt, "help|h" );
-
-if ( $opt{help} ) {
-    require Pod::Usage;
-    Pod::Usage::pod2usage( { verbose => 2 } );
-    exit;
-}
+Init( \%opt,
+    "limit-to-privileged|l",
+    "skip-disabled|s",
+    "all|a",
+);
 
 
-require RT;
 require XML::Simple;
 
 require XML::Simple;
 
-RT::LoadConfig();
-RT::Init();
-
-my $LocalOnly = @ARGV ? shift(@ARGV) : 1;
-
 my %RV;
 my %Ignore = (
     All => [
 my %RV;
 my %Ignore = (
     All => [
@@ -101,11 +87,6 @@ my %Ignore = (
             id Created Creator LastUpdated LastUpdatedBy
             )
            ],
             id Created Creator LastUpdated LastUpdatedBy
             )
            ],
-    Templates => [
-        qw(
-            TranslationOf
-            )
-    ],
 );
 
 my $SystemUserId = RT->SystemUser->Id;
 );
 
 my $SystemUserId = RT->SystemUser->Id;
@@ -114,10 +95,17 @@ my @classes      = qw(
     Templates Scrips ACL CustomFields
     );
 foreach my $class (@classes) {
     Templates Scrips ACL CustomFields
     );
 foreach my $class (@classes) {
-    require "RT/$class.pm";
     my $objects = "RT::$class"->new( RT->SystemUser );
     my $objects = "RT::$class"->new( RT->SystemUser );
-    $objects->{find_disabled_rows} = 1;
+    $objects->{find_disabled_rows} = 1 unless $opt{'skip-disabled'};
     $objects->UnLimit;
     $objects->UnLimit;
+    $objects->LimitToPrivileged if $class eq 'Users'
+        && $opt{'limit-to-privileged'};
+    $objects->Limit(
+        FIELD    => 'Domain',
+        OPERATOR => '=',
+        VALUE    => 'UserDefined',
+        CASESENSITIVE => 0,
+    ) if $class eq 'Groups';
 
     if ( $class eq 'CustomFields' ) {
         $objects->OrderByCols(
 
     if ( $class eq 'CustomFields' ) {
         $objects->OrderByCols(
@@ -129,7 +117,7 @@ foreach my $class (@classes) {
         $objects->OrderBy( FIELD => 'Id' );
     }
 
         $objects->OrderBy( FIELD => 'Id' );
     }
 
-    if ($LocalOnly) {
+    unless ($opt{all}) {
         next if $class eq 'ACL';    # XXX - would go into infinite loop - XXX
         $objects->Limit(
             FIELD    => 'LastUpdatedBy',
         next if $class eq 'ACL';    # XXX - would go into infinite loop - XXX
         $objects->Limit(
             FIELD    => 'LastUpdatedBy',
@@ -141,14 +129,10 @@ foreach my $class (@classes) {
             OPERATOR => '!=',
             VALUE    => $SystemUserId
         ) if $class eq 'Users';
             OPERATOR => '!=',
             VALUE    => $SystemUserId
         ) if $class eq 'Users';
-        $objects->Limit(
-            FIELD    => 'Domain',
-            OPERATOR => '=',
-            VALUE    => 'UserDefined'
-        ) if $class eq 'Groups';
     }
 
     my %fields;
     }
 
     my %fields;
+OBJECT:
     while ( my $obj = $objects->Next ) {
         next
             if $obj->can('LastUpdatedBy')
     while ( my $obj = $objects->Next ) {
         next
             if $obj->can('LastUpdatedBy')
@@ -162,43 +146,117 @@ foreach my $class (@classes) {
 
         my $rv;
 
 
         my $rv;
 
-        # next if $obj-> # skip default names
-        foreach my $field ( sort keys %fields ) {
-            my $value = $obj->__Value($field);
-            $rv->{$field} = $value if ( defined($value) && length($value) );
-        }
-        delete $rv->{Disabled} unless $rv->{Disabled};
-
-        foreach my $record ( map { /ACL/ ? 'ACE' : substr( $_, 0, -1 ) }
-            @classes )
-        {
-            foreach my $key ( map "$record$_", ( '', 'Id' ) ) {
-                next unless exists $rv->{$key};
-                my $id = $rv->{$key} or next;
-                my $obj = "RT::$record"->new( RT->SystemUser );
-                $obj->LoadByCols( Id => $id ) or next;
-                $rv->{$key} = $obj->__Value('Name') || 0;
+        if ( $class ne 'ACL' ) {
+            # next if $obj-> # skip default names
+            foreach my $field ( sort keys %fields ) {
+                my $value = $obj->__Value($field);
+                $rv->{$field} = $value if ( defined($value) && length($value) );
+            }
+            delete $rv->{Disabled} unless $rv->{Disabled};
+
+            foreach my $record ( map { /ACL/ ? 'ACE' : substr( $_, 0, -1 ) }
+                @classes )
+            {
+                foreach my $key ( map "$record$_", ( '', 'Id' ) ) {
+                    next unless exists $rv->{$key};
+                    my $id = $rv->{$key} or next;
+                    next unless $id =~ /^\d+$/;
+                    my $obj = "RT::$record"->new( RT->SystemUser );
+                    $obj->LoadByCols( Id => $id ) or next;
+                    $rv->{$key} = $obj->__Value('Name') || 0;
+                }
+            }
+
+            if ( $class eq 'Users' and defined $obj->Privileged ) {
+                $rv->{Privileged} = int( $obj->Privileged );
+            } elsif ( $class eq 'CustomFields' ) {
+                my $values = $obj->Values;
+                while ( my $value = $values->Next ) {
+                    push @{ $rv->{Values} }, {
+                        map { ( $_ => $value->__Value($_) ) }
+                            qw(
+                            Name Description SortOrder
+                            ),
+                    };
+                }
+                if ( $obj->LookupType eq 'RT::Queue-RT::Ticket' ) {
+                    # XXX-TODO: unused CF's turn into global CF when importing
+                    # as the sub InsertData in RT::Handle creates a global CF
+                    # when no queue is specified.
+                    $rv->{Queue} = [];
+                    my $applies = $obj->AppliedTo;
+                    while ( my $queue = $applies->Next ) {
+                        push @{ $rv->{Queue} }, $queue->Name;
+                    }
+                }
             }
         }
             }
         }
+        else {
+            # 1) pick the right
+            $rv->{Right} = $obj->RightName;
+
+            # 2) Pick a level: Granted on Queue, CF, CF+Queue, or Globally?
+            for ( $obj->ObjectType ) {
+                if ( /^RT::Queue$/ ) {
+                    next OBJECT if $opt{'skip-disabled'} && $obj->Object->Disabled;
+                    $rv->{Queue} = $obj->Object->Name;
+                }
+                elsif ( /^RT::CustomField$/ ) {
+                    next OBJECT if $opt{'skip-disabled'} && $obj->Object->Disabled;
+                    $rv->{CF} = $obj->Object->Name;
+                }
+                elsif ( /^RT::Group$/ ) {
+                    # No support for RT::Group ACLs in RT::Handle yet.
+                    next OBJECT;
+                }
+                elsif ( /^RT::System$/ ) {
+                    # skip setting anything on $rv;
+                    # "Specifying none of the above will get you a global right."
+                }
+            }
 
 
-        if ( $class eq 'Users' and defined $obj->Privileged ) {
-            $rv->{Privileged} = int( $obj->Privileged );
-        } elsif ( $class eq 'CustomFields' ) {
-            my $values = $obj->Values;
-            while ( my $value = $values->Next ) {
-                push @{ $rv->{Values} }, {
-                    map { ( $_ => $value->__Value($_) ) }
-                        qw(
-                        Name Description SortOrder
-                        ),
-                };
+            # 3) Pick a Principal; User or Group or Role
+            if ( $obj->PrincipalType eq 'Group' ) {
+                next OBJECT if $opt{'skip-disabled'} && $obj->PrincipalObj->Disabled;
+                my $group = $obj->PrincipalObj->Object;
+                for ( $group->Domain ) {
+                    # An internal user group
+                    if ( /^SystemInternal$/ ) {
+                        $rv->{GroupDomain} = $group->Domain;
+                        $rv->{GroupType}   = $group->Name;
+                    }
+                    # An individual user
+                    elsif ( /^ACLEquivalence$/ ) {
+                        my $member = $group->MembersObj->Next->MemberObj;
+                        next OBJECT if $opt{'skip-disabled'} && $member->Disabled;
+                        $rv->{UserId} = $member->Object->Name;
+                    }
+                    # A group you created
+                    elsif ( /^UserDefined$/ ) {
+                        $rv->{GroupDomain} = 'UserDefined';
+                        $rv->{GroupId} = $group->Name;
+                    }
+                }
+            } else {
+                $rv->{GroupType} = $obj->PrincipalType;
+                # A system-level role
+                if ( $obj->ObjectType eq 'RT::System' ) {
+                    $rv->{GroupDomain} = 'RT::System-Role';
+                }
+                # A queue-level role
+                elsif ( $obj->ObjectType eq 'RT::Queue' ) {
+                    $rv->{GroupDomain} = 'RT::Queue-Role';
+                }
             }
         }
 
             }
         }
 
-        if ( eval { require RT::Attributes; 1 } ) {
+        if ( RT::Attributes->require ) {
             my $attributes = $obj->Attributes;
             while ( my $attribute = $attributes->Next ) {
                 my $content = $attribute->Content;
             my $attributes = $obj->Attributes;
             while ( my $attribute = $attributes->Next ) {
                 my $content = $attribute->Content;
+                if ( $class eq 'Users' and $attribute->Name eq 'Bookmarks' ) {
+                    next;
+                }
                 $rv->{Attributes}{ $attribute->Name } = $content
                     if length($content);
             }
                 $rv->{Attributes}{ $attribute->Name } = $content
                     if length($content);
             }
@@ -231,7 +289,7 @@ rt-dump-metadata - dump configuration metadata from an RT database
 
 =head1 SYNOPSIS
 
 
 =head1 SYNOPSIS
 
-    rt-dump-metdata [ 0 ]
+    rt-dump-metdata [--all]
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
@@ -241,11 +299,28 @@ C<rt-setup-database>. To dump and load a full RT database, you should generally
 use the native database tools instead, as well as performing any necessary
 steps from UPGRADING.
 
 use the native database tools instead, as well as performing any necessary
 steps from UPGRADING.
 
-When run without arguments, the metadata dump will only include 'local'
+This is NOT a tool for backing up an RT database.  See also
+L<docs/initialdata> for more straightforward means of importing data.
+
+=head1 OPTIONS
+
+=over
+
+=item C<--all> or C<-a>
+
+When run with C<--all>, the dump will include all configuration
+metadata; otherwise, the metadata dump will only include 'local'
 configuration changes, i.e. those done manually in the web interface.
 
 configuration changes, i.e. those done manually in the web interface.
 
-When run with the argument '0', the dump will include all configuration
-metadata.
+=item C<--limit-to-privileged> or C<-l>
+
+Causes the dumper to only dump privileged users.
+
+=item C<--skip-disabled> or C<-s>
+
+Ignores disabled rows in the database.
+
+=back
 
 
-This is NOT a tool for backing up an RT database.
+=cut