[Rt-commit] rt branch, 4.2/link-api-refactoring, created. rt-4.0.6-462-g8fce29d

Thomas Sibley trs at bestpractical.com
Tue Nov 20 16:48:28 EST 2012


The branch, 4.2/link-api-refactoring has been created
        at  8fce29da0d28ed9ccbfff21f5b94b5c61e980364 (commit)

- Log -----------------------------------------------------------------
commit 92d78a8606c28c265f5398541864db66e53c95d4
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Aug 17 09:29:19 2012 -0700

    Code formatting, no functional changes

diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index e788e54..b772a25 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1264,8 +1264,6 @@ sub FormatLink {
     return $text;
 }
 
-
-
 =head2 _AddLink
 
 Takes a paramhash of Type and one of Base or Target. Adds that link to this object.
@@ -1277,12 +1275,13 @@ Returns C<link id>, C<message> and C<exist> flag.
 
 sub _AddLink {
     my $self = shift;
-    my %args = ( Target => '',
-                 Base   => '',
-                 Type   => '',
-                 Silent => undef,
-                 @_ );
-
+    my %args = (
+        Target       => '',
+        Base         => '',
+        Type         => '',
+        Silent       => undef,
+        @_
+    );
 
     # Remote_link is the URI of the object that is not this ticket
     my $remote_link;
@@ -1317,32 +1316,26 @@ sub _AddLink {
         return ( $old_link->id, $self->loc("Link already exists"), 1 );
     }
 
-    # }}}
-
-
     # Storing the link in the DB.
     my $link = RT::Link->new( $self->CurrentUser );
     my ($linkid, $linkmsg) = $link->Create( Target => $args{Target},
-                                  Base   => $args{Base},
-                                  Type   => $args{Type} );
+                                            Base   => $args{Base},
+                                            Type   => $args{Type} );
 
     unless ($linkid) {
         $RT::Logger->error("Link could not be created: ".$linkmsg);
         return ( 0, $self->loc("Link could not be created") );
     }
 
-    my $basetext = $self->FormatLink(Object => $link->BaseObj,
-				     FallBack => $args{Base});
-    my $targettext = $self->FormatLink(Object => $link->TargetObj,
-				       FallBack => $args{Target});
+    my $basetext = $self->FormatLink(Object   => $link->BaseObj,
+                                     FallBack => $args{Base});
+    my $targettext = $self->FormatLink(Object   => $link->TargetObj,
+                                       FallBack => $args{Target});
     my $typetext = $self->FormatType(Type => $args{Type});
-    my $TransString =
-      "$basetext $typetext $targettext.";
+    my $TransString = "$basetext $typetext $targettext.";
     return ( $linkid, $TransString ) ;
 }
 
-
-
 =head2 _DeleteLink
 
 Delete a link. takes a paramhash of Base, Target and Type.
@@ -1360,9 +1353,7 @@ sub _DeleteLink {
         @_
     );
 
-    #we want one of base and target. we don't care which
-    #but we only want _one_
-
+    # We want one of base and target. We don't care which but we only want _one_.
     my $direction;
     my $remote_link;
 
@@ -1372,13 +1363,13 @@ sub _DeleteLink {
     }
     elsif ( $args{'Base'} ) {
         $args{'Target'} = $self->URI();
-	$remote_link = $args{'Base'};
-    	$direction = 'Target';
+        $remote_link    = $args{'Base'};
+        $direction      = 'Target';
     }
     elsif ( $args{'Target'} ) {
         $args{'Base'} = $self->URI();
-	$remote_link = $args{'Target'};
-        $direction='Base';
+        $remote_link  = $args{'Target'};
+        $direction    = 'Base';
     }
     else {
         $RT::Logger->error("Base or Target must be specified");
@@ -1386,17 +1377,23 @@ sub _DeleteLink {
     }
 
     my $link = RT::Link->new( $self->CurrentUser );
-    $RT::Logger->debug( "Trying to load link: " . $args{'Base'} . " " . $args{'Type'} . " " . $args{'Target'} );
-
-
-    $link->LoadByParams( Base=> $args{'Base'}, Type=> $args{'Type'}, Target=>  $args{'Target'} );
-    #it's a real link. 
+    $RT::Logger->debug( "Trying to load link: "
+            . $args{'Base'} . " "
+            . $args{'Type'} . " "
+            . $args{'Target'} );
+
+    $link->LoadByParams(
+        Base   => $args{'Base'},
+        Type   => $args{'Type'},
+        Target => $args{'Target'}
+    );
 
+    # it's a real link.
     if ( $link->id ) {
-        my $basetext = $self->FormatLink(Object => $link->BaseObj,
-                                     FallBack => $args{Base});
-        my $targettext = $self->FormatLink(Object => $link->TargetObj,
-                                       FallBack => $args{Target});
+        my $basetext = $self->FormatLink(Object   => $link->BaseObj,
+                                         FallBack => $args{Base});
+        my $targettext = $self->FormatLink(Object   => $link->TargetObj,
+                                           FallBack => $args{Target});
         my $typetext = $self->FormatType(Type => $args{Type});
         my $linkid = $link->id;
         $link->Delete();
@@ -1411,10 +1408,6 @@ sub _DeleteLink {
     }
 }
 
-
-
-
-
 =head2 _NewTransaction  PARAMHASH
 
 Private function to create a new RT::Transaction object for this ticket update

commit 7821ff1e221032408f6bad2386618ce96df5519f
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Aug 17 11:22:11 2012 -0700

    Move creation of link transactions from RT::Ticket into RT::Record
    
    Now articles and other objects will record transactions for links.

diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index b772a25..6e6e87d 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1268,8 +1268,11 @@ sub FormatLink {
 
 Takes a paramhash of Type and one of Base or Target. Adds that link to this object.
 
-Returns C<link id>, C<message> and C<exist> flag.
+If Silent is true then no transactions will be recorded.  You can individually
+control transactions on both base and target and with SilentBase and
+SilentTarget respectively. By default both transactions are created.
 
+Returns a tuple of (link ID, message, flag if link already existed).
 
 =cut
 
@@ -1280,6 +1283,9 @@ sub _AddLink {
         Base         => '',
         Type         => '',
         Silent       => undef,
+        Silent       => undef,
+        SilentBase   => undef,
+        SilentTarget => undef,
         @_
     );
 
@@ -1324,7 +1330,7 @@ sub _AddLink {
 
     unless ($linkid) {
         $RT::Logger->error("Link could not be created: ".$linkmsg);
-        return ( 0, $self->loc("Link could not be created") );
+        return ( 0, $self->loc("Link could not be created: [_1]", $linkmsg) );
     }
 
     my $basetext = $self->FormatLink(Object   => $link->BaseObj,
@@ -1333,23 +1339,62 @@ sub _AddLink {
                                        FallBack => $args{Target});
     my $typetext = $self->FormatType(Type => $args{Type});
     my $TransString = "$basetext $typetext $targettext.";
-    return ( $linkid, $TransString ) ;
+
+    # No transactions for you!
+    return ($linkid, $TransString) if $args{'Silent'};
+
+    # Some transactions?
+    my $remote_uri = RT::URI->new( $self->CurrentUser );
+    $remote_uri->FromURI( $remote_link );
+
+    my $opposite_direction = $direction eq 'Target' ? 'Base': 'Target';
+
+    unless ( $args{ 'Silent'. $direction } ) {
+        my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+            Type      => 'AddLink',
+            Field     => $LINKDIRMAP{$args{'Type'}}->{$direction},
+            NewValue  => $remote_uri->URI || $remote_link,
+            TimeTaken => 0
+        );
+        $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans;
+    }
+
+    if ( !$args{"Silent$opposite_direction"} && $remote_uri->IsLocal ) {
+        my $OtherObj = $remote_uri->Object;
+        my ( $val, $msg ) = $OtherObj->_NewTransaction(
+            Type           => 'AddLink',
+            Field          => $LINKDIRMAP{$args{'Type'}}->{$opposite_direction},
+            NewValue       => $self->URI,
+            ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
+            TimeTaken      => 0,
+        );
+        $RT::Logger->error("Couldn't create transaction: $msg") unless $val;
+    }
+
+    return ($linkid, $TransString);
 }
 
 =head2 _DeleteLink
 
-Delete a link. takes a paramhash of Base, Target and Type.
-Either Base or Target must be null. The null value will 
-be replaced with this ticket\'s id
+Takes a paramhash of Type and one of Base or Target. Removes that link from this object.
+
+If Silent is true then no transactions will be recorded.  You can individually
+control transactions on both base and target and with SilentBase and
+SilentTarget respectively. By default both transactions are created.
+
+Returns a tuple of (status flag, message).
 
 =cut 
 
 sub _DeleteLink {
     my $self = shift;
     my %args = (
-        Base   => undef,
-        Target => undef,
-        Type   => undef,
+        Base         => undef,
+        Target       => undef,
+        Type         => undef,
+        Silent       => undef,
+        SilentBase   => undef,
+        SilentTarget => undef,
         @_
     );
 
@@ -1388,24 +1433,56 @@ sub _DeleteLink {
         Target => $args{'Target'}
     );
 
-    # it's a real link.
-    if ( $link->id ) {
-        my $basetext = $self->FormatLink(Object   => $link->BaseObj,
-                                         FallBack => $args{Base});
-        my $targettext = $self->FormatLink(Object   => $link->TargetObj,
-                                           FallBack => $args{Target});
-        my $typetext = $self->FormatType(Type => $args{Type});
-        my $linkid = $link->id;
-        $link->Delete();
-        my $TransString = "$basetext no longer $typetext $targettext.";
-        return ( 1, $TransString);
-    }
-
-    #if it's not a link we can find
-    else {
+    unless ($link->id) {
         $RT::Logger->debug("Couldn't find that link");
         return ( 0, $self->loc("Link not found") );
     }
+
+    my $basetext = $self->FormatLink(Object   => $link->BaseObj,
+                                     FallBack => $args{Base});
+    my $targettext = $self->FormatLink(Object   => $link->TargetObj,
+                                       FallBack => $args{Target});
+    my $typetext = $self->FormatType(Type => $args{Type});
+    my $TransString = "$basetext no longer $typetext $targettext.";
+
+    my ($ok, $msg) = $link->Delete();
+    unless ($ok) {
+        RT->Logger->error("Link could not be deleted: $msg");
+        return ( 0, $self->loc("Link could not be deleted: [_1]", $msg) );
+    }
+
+    # No transactions for you!
+    return (1, $TransString) if $args{'Silent'};
+
+    # Some transactions?
+    my $remote_uri = RT::URI->new( $self->CurrentUser );
+    $remote_uri->FromURI( $remote_link );
+
+    my $opposite_direction = $direction eq 'Target' ? 'Base': 'Target';
+
+    unless ( $args{ 'Silent'. $direction } ) {
+        my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+            Type      => 'DeleteLink',
+            Field     => $LINKDIRMAP{$args{'Type'}}->{$direction},
+            OldValue  => $remote_uri->URI || $remote_link,
+            TimeTaken => 0
+        );
+        $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans;
+    }
+
+    if ( !$args{"Silent$opposite_direction"} && $remote_uri->IsLocal ) {
+        my $OtherObj = $remote_uri->Object;
+        my ( $val, $msg ) = $OtherObj->_NewTransaction(
+            Type           => 'DeleteLink',
+            Field          => $LINKDIRMAP{$args{'Type'}}->{$opposite_direction},
+            OldValue       => $self->URI,
+            ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
+            TimeTaken      => 0,
+        );
+        $RT::Logger->error("Couldn't create transaction: $msg") unless $val;
+    }
+
+    return (1, $TransString);
 }
 
 =head2 _NewTransaction  PARAMHASH
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index cf6ac40..ae93816 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -2413,14 +2413,10 @@ sub _Links {
 
 =head2 DeleteLink
 
-Delete a link. takes a paramhash of Base, Target, Type, Silent,
-SilentBase and SilentTarget. Either Base or Target must be null.
-The null value will be replaced with this ticket\'s id.
+Takes a paramhash of Type and one of Base or Target. Removes that link from this ticket.
 
-If Silent is true then no transaction would be recorded, in other
-case you can control creation of transactions on both base and
-target with SilentBase and SilentTarget respectively. By default
-both transactions are created.
+Refer to L<RT::Record/_DeleteLink> for full documentation.  This method
+implements permission checks before calling into L<RT::Record>.
 
 =cut 
 
@@ -2429,10 +2425,6 @@ sub DeleteLink {
     my %args = (
         Base   => undef,
         Target => undef,
-        Type   => undef,
-        Silent => undef,
-        SilentBase   => undef,
-        SilentTarget => undef,
         @_
     );
 
@@ -2461,61 +2453,15 @@ sub DeleteLink {
         return ( 0, $self->loc("Permission Denied") );
     }
 
-    my ($val, $Msg) = $self->SUPER::_DeleteLink(%args);
-    return ( 0, $Msg ) unless $val;
-
-    return ( $val, $Msg ) if $args{'Silent'};
-
-    my ($direction, $remote_link);
-
-    if ( $args{'Base'} ) {
-        $remote_link = $args{'Base'};
-        $direction = 'Target';
-    }
-    elsif ( $args{'Target'} ) {
-        $remote_link = $args{'Target'};
-        $direction = 'Base';
-    } 
-
-    my $remote_uri = RT::URI->new( $self->CurrentUser );
-    $remote_uri->FromURI( $remote_link );
-
-    unless ( $args{ 'Silent'. $direction } ) {
-        my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
-            Type      => 'DeleteLink',
-            Field     => $LINKDIRMAP{$args{'Type'}}->{$direction},
-            OldValue  => $remote_uri->URI || $remote_link,
-            TimeTaken => 0
-        );
-        $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans;
-    }
-
-    if ( !$args{ 'Silent'. ( $direction eq 'Target'? 'Base': 'Target' ) } && $remote_uri->IsLocal ) {
-        my $OtherObj = $remote_uri->Object;
-        my ( $val, $Msg ) = $OtherObj->_NewTransaction(
-            Type           => 'DeleteLink',
-            Field          => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
-                                            : $LINKDIRMAP{$args{'Type'}}->{Target},
-            OldValue       => $self->URI,
-            ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
-            TimeTaken      => 0,
-        );
-        $RT::Logger->error("Couldn't create transaction: $Msg") unless $val;
-    }
-
-    return ( $val, $Msg );
+    return $self->_DeleteLink(%args);
 }
 
-
-
 =head2 AddLink
 
 Takes a paramhash of Type and one of Base or Target. Adds that link to this ticket.
 
-If Silent is true then no transaction would be recorded, in other
-case you can control creation of transactions on both base and
-target with SilentBase and SilentTarget respectively. By default
-both transactions are created.
+Refer to L<RT::Record/_AddLink> for full documentation.  This method implements
+permissions and ticket validity checks before calling into L<RT::Record>.
 
 =cut
 
@@ -2580,67 +2526,6 @@ sub __GetTicketFromURI {
     return (1, 'Found ticket', $obj);
 }
 
-=head2 _AddLink  
-
-Private non-acled variant of AddLink so that links can be added during create.
-
-=cut
-
-sub _AddLink {
-    my $self = shift;
-    my %args = ( Target       => '',
-                 Base         => '',
-                 Type         => '',
-                 Silent       => undef,
-                 SilentBase   => undef,
-                 SilentTarget => undef,
-                 @_ );
-
-    my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args);
-    return ($val, $msg) if !$val || $exist;
-    return ($val, $msg) if $args{'Silent'};
-
-    my ($direction, $remote_link);
-    if ( $args{'Target'} ) {
-        $remote_link  = $args{'Target'};
-        $direction    = 'Base';
-    } elsif ( $args{'Base'} ) {
-        $remote_link  = $args{'Base'};
-        $direction    = 'Target';
-    }
-
-    my $remote_uri = RT::URI->new( $self->CurrentUser );
-    $remote_uri->FromURI( $remote_link );
-
-    unless ( $args{ 'Silent'. $direction } ) {
-        my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
-            Type      => 'AddLink',
-            Field     => $LINKDIRMAP{$args{'Type'}}->{$direction},
-            NewValue  =>  $remote_uri->URI || $remote_link,
-            TimeTaken => 0
-        );
-        $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans;
-    }
-
-    if ( !$args{ 'Silent'. ( $direction eq 'Target'? 'Base': 'Target' ) } && $remote_uri->IsLocal ) {
-        my $OtherObj = $remote_uri->Object;
-        my ( $val, $msg ) = $OtherObj->_NewTransaction(
-            Type           => 'AddLink',
-            Field          => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
-                                            : $LINKDIRMAP{$args{'Type'}}->{Target},
-            NewValue       => $self->URI,
-            ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
-            TimeTaken      => 0,
-        );
-        $RT::Logger->error("Couldn't create transaction: $msg") unless $val;
-    }
-
-    return ( $val, $msg );
-}
-
-
-
-
 =head2 MergeInto
 
 MergeInto take the id of the ticket to merge this ticket into.

commit 4effe0cca3dc163e2d4444432245c48db6354f10
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Aug 17 13:16:03 2012 -0700

    Refactor our link type and direction maps into RT::Link
    
    A single definition is much cleaner and easier to maintain than
    duplicates scattered among the classes or pointers into RT::Ticket.
    
    Any existing non-core code which needs the maps will need to be updated
    to point to RT::Link.

diff --git a/docs/UPGRADING-4.2 b/docs/UPGRADING-4.2
index 66af083..ed52460 100644
--- a/docs/UPGRADING-4.2
+++ b/docs/UPGRADING-4.2
@@ -4,3 +4,9 @@ UPGRADING FROM RT 4.0.0 and greater
   describes what the log level controls.  Setting $LogToScreen will still work,
   but an informational notice will be issued on server start telling you about
   the rename.  To avoid this you should set $LogToSTDERR instead.
+
+* The link direction and type maps are consolidated into RT::Link.  If you
+  wrote local customizations or extensions utilizing %RT::Ticket::LINKDIRMAP,
+  %RT::Ticket::LINKTYPEMAP, RT::Ticket->LINKDIRMAP, RT::Ticket->LINKTYPEMAP, or
+  %RT::Record::LINKDIRMAP, you will need to switch to %RT::Link::DIRMAP and
+  %RT::Link::TYPEMAP.
diff --git a/lib/RT/Action/CreateTickets.pm b/lib/RT/Action/CreateTickets.pm
index 298f7e9..38028cb 100644
--- a/lib/RT/Action/CreateTickets.pm
+++ b/lib/RT/Action/CreateTickets.pm
@@ -53,6 +53,7 @@ use strict;
 use warnings;
 
 use MIME::Entity;
+use RT::Link;
 
 =head1 NAME
 
@@ -253,47 +254,6 @@ perl(1).
 
 =cut
 
-my %LINKTYPEMAP = (
-    MemberOf => {
-        Type => 'MemberOf',
-        Mode => 'Target',
-    },
-    Parents => {
-        Type => 'MemberOf',
-        Mode => 'Target',
-    },
-    Members => {
-        Type => 'MemberOf',
-        Mode => 'Base',
-    },
-    Children => {
-        Type => 'MemberOf',
-        Mode => 'Base',
-    },
-    HasMember => {
-        Type => 'MemberOf',
-        Mode => 'Base',
-    },
-    RefersTo => {
-        Type => 'RefersTo',
-        Mode => 'Target',
-    },
-    ReferredToBy => {
-        Type => 'RefersTo',
-        Mode => 'Base',
-    },
-    DependsOn => {
-        Type => 'DependsOn',
-        Mode => 'Target',
-    },
-    DependedOnBy => {
-        Type => 'DependsOn',
-        Mode => 'Base',
-    },
-
-);
-
-
 #Do what we need to do and send it out.
 sub Commit {
     my $self = shift;
@@ -721,7 +681,7 @@ sub ParseLines {
                 }
                 if (
                     ($tag =~ /^(requestor|cc|admincc)(group)?$/i
-                        or grep {lc $_ eq $tag} keys %LINKTYPEMAP)
+                        or grep {lc $_ eq $tag} keys %RT::Link::TYPEMAP)
                     and $args{$tag} =~ /,/
                 ) {
                     $args{$tag} = [ split /,\s*/, $args{$tag} ];
@@ -1006,7 +966,7 @@ sub GetUpdateTemplate {
     $string .= "InitialPriority: " . $t->Priority . "\n";
     $string .= "FinalPriority: " . $t->FinalPriority . "\n";
 
-    foreach my $type ( sort keys %LINKTYPEMAP ) {
+    foreach my $type ( sort keys %RT::Link::TYPEMAP ) {
 
         # don't display duplicates
         if (   $type eq "HasMember"
@@ -1017,8 +977,8 @@ sub GetUpdateTemplate {
         }
         $string .= "$type: ";
 
-        my $mode   = $LINKTYPEMAP{$type}->{Mode};
-        my $method = $LINKTYPEMAP{$type}->{Type};
+        my $mode   = $RT::Link::TYPEMAP{$type}->{Mode};
+        my $method = $RT::Link::TYPEMAP{$type}->{Type};
 
         my $links = '';
         while ( my $link = $t->$method->Next ) {
@@ -1084,7 +1044,7 @@ sub GetCreateTemplate {
     $string .= "InitialPriority: \n";
     $string .= "FinalPriority: \n";
 
-    foreach my $type ( keys %LINKTYPEMAP ) {
+    foreach my $type ( keys %RT::Link::TYPEMAP ) {
 
         # don't display duplicates
         if (   $type eq "HasMember"
@@ -1214,7 +1174,7 @@ sub PostProcess {
         $RT::Logger->debug( "Handling links for " . $ticket->Id );
         my %args = %{ shift(@$links) };
 
-        foreach my $type ( keys %LINKTYPEMAP ) {
+        foreach my $type ( keys %RT::Link::TYPEMAP ) {
             next unless ( defined $args{$type} );
             foreach my $link (
                 ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
@@ -1241,8 +1201,8 @@ sub PostProcess {
                 }
 
                 my ( $wval, $wmsg ) = $ticket->AddLink(
-                    Type => $LINKTYPEMAP{$type}->{'Type'},
-                    $LINKTYPEMAP{$type}->{'Mode'} => $link,
+                    Type => $RT::Link::TYPEMAP{$type}->{'Type'},
+                    $RT::Link::TYPEMAP{$type}->{'Mode'} => $link,
                     Silent                        => 1
                 );
 
diff --git a/lib/RT/Link.pm b/lib/RT/Link.pm
index b26f564..f1d0139 100644
--- a/lib/RT/Link.pm
+++ b/lib/RT/Link.pm
@@ -79,6 +79,26 @@ sub Table {'Links'}
 use Carp;
 use RT::URI;
 
+# Helper tables for links mapping to make it easier
+# to build and parse links between objects.
+our %TYPEMAP = (
+    MemberOf        => { Type => 'MemberOf',    Mode => 'Target' },
+    Parents         => { Type => 'MemberOf',    Mode => 'Target' },
+    Members         => { Type => 'MemberOf',    Mode => 'Base'   },
+    Children        => { Type => 'MemberOf',    Mode => 'Base'   },
+    HasMember       => { Type => 'MemberOf',    Mode => 'Base'   },
+    RefersTo        => { Type => 'RefersTo',    Mode => 'Target' },
+    ReferredToBy    => { Type => 'RefersTo',    Mode => 'Base'   },
+    DependsOn       => { Type => 'DependsOn',   Mode => 'Target' },
+    DependedOnBy    => { Type => 'DependsOn',   Mode => 'Base'   },
+    MergedInto      => { Type => 'MergedInto',  Mode => 'Target' },
+);
+our %DIRMAP = (
+    MemberOf    => { Base => 'MemberOf',    Target => 'HasMember'    },
+    RefersTo    => { Base => 'RefersTo',    Target => 'ReferredToBy' },
+    DependsOn   => { Base => 'DependsOn',   Target => 'DependedOnBy' },
+    MergedInto  => { Base => 'MergedInto',  Target => 'MergedInto'   },
+);
 
 
 =head2 Create PARAMHASH
diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 6e6e87d..3b524f8 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -70,6 +70,7 @@ use warnings;
 use RT::Date;
 use RT::User;
 use RT::Attributes;
+use RT::Link;
 use Encode qw();
 
 our $_TABLE_ATTR = { };
@@ -820,23 +821,6 @@ sub _DecodeLOB {
         return ($Content);
 }
 
-# A helper table for links mapping to make it easier
-# to build and parse links between tickets
-
-use vars '%LINKDIRMAP';
-
-%LINKDIRMAP = (
-    MemberOf => { Base => 'MemberOf',
-                  Target => 'HasMember', },
-    RefersTo => { Base => 'RefersTo',
-                Target => 'ReferredToBy', },
-    DependsOn => { Base => 'DependsOn',
-                   Target => 'DependedOnBy', },
-    MergedInto => { Base => 'MergedInto',
-                   Target => 'MergedInto', },
-
-);
-
 =head2 Update  ARGSHASH
 
 Updates fields on an object for you using the proper Set methods,
@@ -1352,7 +1336,7 @@ sub _AddLink {
     unless ( $args{ 'Silent'. $direction } ) {
         my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
             Type      => 'AddLink',
-            Field     => $LINKDIRMAP{$args{'Type'}}->{$direction},
+            Field     => $RT::Link::DIRMAP{$args{'Type'}}->{$direction},
             NewValue  => $remote_uri->URI || $remote_link,
             TimeTaken => 0
         );
@@ -1363,7 +1347,7 @@ sub _AddLink {
         my $OtherObj = $remote_uri->Object;
         my ( $val, $msg ) = $OtherObj->_NewTransaction(
             Type           => 'AddLink',
-            Field          => $LINKDIRMAP{$args{'Type'}}->{$opposite_direction},
+            Field          => $RT::Link::DIRMAP{$args{'Type'}}->{$opposite_direction},
             NewValue       => $self->URI,
             ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
             TimeTaken      => 0,
@@ -1463,7 +1447,7 @@ sub _DeleteLink {
     unless ( $args{ 'Silent'. $direction } ) {
         my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
             Type      => 'DeleteLink',
-            Field     => $LINKDIRMAP{$args{'Type'}}->{$direction},
+            Field     => $RT::Link::DIRMAP{$args{'Type'}}->{$direction},
             OldValue  => $remote_uri->URI || $remote_link,
             TimeTaken => 0
         );
@@ -1474,7 +1458,7 @@ sub _DeleteLink {
         my $OtherObj = $remote_uri->Object;
         my ( $val, $msg ) = $OtherObj->_NewTransaction(
             Type           => 'DeleteLink',
-            Field          => $LINKDIRMAP{$args{'Type'}}->{$opposite_direction},
+            Field          => $RT::Link::DIRMAP{$args{'Type'}}->{$opposite_direction},
             OldValue       => $self->URI,
             ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
             TimeTaken      => 0,
diff --git a/lib/RT/Shredder/Link.pm b/lib/RT/Shredder/Link.pm
index 94beb86..e47259c 100644
--- a/lib/RT/Shredder/Link.pm
+++ b/lib/RT/Shredder/Link.pm
@@ -72,7 +72,7 @@ sub __DependsOn
     my $list = [];
 
 # AddLink transactions
-    my $map = RT::Ticket->LINKTYPEMAP;
+    my $map = { %RT::Link::TYPEMAP };
     my $link_meta = $map->{ $self->Type };
     unless ( $link_meta && $link_meta->{'Mode'} && $link_meta->{'Type'} ) {
         RT::Shredder::Exception->throw( 'Wrong link link_meta, no record for '. $self->Type );
diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index d9c43ab..4b90855 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -722,7 +722,7 @@ sub create_tickets {
     while ( @data ) {
         my %args = %{ shift @data };
         $args{$_} = $res[ $args{$_} ]->id foreach
-            grep $args{ $_ }, keys %RT::Ticket::LINKTYPEMAP;
+            grep $args{ $_ }, keys %RT::Link::TYPEMAP;
         push @res, $self->create_ticket( %$defaults, %args );
     }
     return @res;
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index ae93816..18c22cb 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -72,6 +72,7 @@ use warnings;
 use RT::Queue;
 use RT::User;
 use RT::Record;
+use RT::Link;
 use RT::Links;
 use RT::Date;
 use RT::CustomFields;
@@ -83,54 +84,6 @@ use RT::URI;
 use MIME::Entity;
 use Devel::GlobalDestruction;
 
-
-# A helper table for links mapping to make it easier
-# to build and parse links between tickets
-
-our %LINKTYPEMAP = (
-    MemberOf => { Type => 'MemberOf',
-                  Mode => 'Target', },
-    Parents => { Type => 'MemberOf',
-         Mode => 'Target', },
-    Members => { Type => 'MemberOf',
-                 Mode => 'Base', },
-    Children => { Type => 'MemberOf',
-          Mode => 'Base', },
-    HasMember => { Type => 'MemberOf',
-                   Mode => 'Base', },
-    RefersTo => { Type => 'RefersTo',
-                  Mode => 'Target', },
-    ReferredToBy => { Type => 'RefersTo',
-                      Mode => 'Base', },
-    DependsOn => { Type => 'DependsOn',
-                   Mode => 'Target', },
-    DependedOnBy => { Type => 'DependsOn',
-                      Mode => 'Base', },
-    MergedInto => { Type => 'MergedInto',
-                   Mode => 'Target', },
-
-);
-
-
-# A helper table for links mapping to make it easier
-# to build and parse links between tickets
-
-our %LINKDIRMAP = (
-    MemberOf => { Base => 'MemberOf',
-                  Target => 'HasMember', },
-    RefersTo => { Base => 'RefersTo',
-                Target => 'ReferredToBy', },
-    DependsOn => { Base => 'DependsOn',
-                   Target => 'DependedOnBy', },
-    MergedInto => { Base => 'MergedInto',
-                   Target => 'MergedInto', },
-
-);
-
-
-sub LINKTYPEMAP   { return \%LINKTYPEMAP   }
-sub LINKDIRMAP   { return \%LINKDIRMAP   }
-
 our %MERGE_CACHE = (
     effective => {},
     merged => {},
@@ -608,7 +561,7 @@ sub Create {
     #
     # //RUZ
 
-    foreach my $type ( keys %LINKTYPEMAP ) {
+    foreach my $type ( keys %RT::Link::TYPEMAP ) {
         next unless ( defined $args{$type} );
         foreach my $link (
             ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
@@ -635,10 +588,10 @@ sub Create {
             }
 
             my ( $wval, $wmsg ) = $self->_AddLink(
-                Type                          => $LINKTYPEMAP{$type}->{'Type'},
-                $LINKTYPEMAP{$type}->{'Mode'} => $link,
+                Type                          => $RT::Link::TYPEMAP{$type}->{'Type'},
+                $RT::Link::TYPEMAP{$type}->{'Mode'} => $link,
                 Silent                        => !$args{'_RecordTransaction'} || $self->Type eq 'reminder',
-                'Silent'. ( $LINKTYPEMAP{$type}->{'Mode'} eq 'Base'? 'Target': 'Base' )
+                'Silent'. ( $RT::Link::TYPEMAP{$type}->{'Mode'} eq 'Base'? 'Target': 'Base' )
                                               => 1,
             );
 
diff --git a/share/html/Elements/RT__Ticket/ColumnMap b/share/html/Elements/RT__Ticket/ColumnMap
index 31f7cd2..fcecb01 100644
--- a/share/html/Elements/RT__Ticket/ColumnMap
+++ b/share/html/Elements/RT__Ticket/ColumnMap
@@ -57,8 +57,8 @@ my $COLUMN_MAP;
 my $LinkCallback = sub {
     my $method = shift;
 
-    my $mode            = $RT::Ticket::LINKTYPEMAP{$method}{Mode};
-    my $type            = $RT::Ticket::LINKTYPEMAP{$method}{Type};
+    my $mode            = $RT::Link::TYPEMAP{$method}{Mode};
+    my $type            = $RT::Link::TYPEMAP{$method}{Type};
     my $other_mode      = ($mode eq "Target" ? "Base" : "Target");
     my $mode_uri        = $mode.'URI';
     my $local_type      = 'Local'.$mode;
@@ -291,7 +291,7 @@ $COLUMN_MAP = {
     # Everything from LINKTYPEMAP
     (map {
         $_ => { value => $LinkCallback->( $_ ) }
-    } keys %RT::Ticket::LINKTYPEMAP),
+    } keys %RT::Link::TYPEMAP),
 
     '_CLASS' => {
         value => sub { return $_[1] % 2 ? 'oddline' : 'evenline' }
diff --git a/share/html/Elements/ShowLinks b/share/html/Elements/ShowLinks
index 61628de..9a9536c 100755
--- a/share/html/Elements/ShowLinks
+++ b/share/html/Elements/ShowLinks
@@ -156,8 +156,8 @@ my $can_create = $Ticket->QueueObj->CurrentUserHasRight('CreateTicket');
 
 for my $relation (
     qw(RefersTo ReferredToBy)) {
-    my $mode  = $RT::Ticket::LINKTYPEMAP{$relation}->{Mode};
-    my $type  = $RT::Ticket::LINKTYPEMAP{$relation}->{Type};
+    my $mode  = $RT::Link::TYPEMAP{$relation}->{Mode};
+    my $type  = $RT::Link::TYPEMAP{$relation}->{Type};
     my $other = "Local" . $mode;
     my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
     $clone->{$field} 
@@ -167,8 +167,8 @@ for my $relation (
 }
 
 for my $relation ( qw(MemberOf Members DependsOn DependedOnBy)) {
-    my $mode  = $RT::Ticket::LINKTYPEMAP{$relation}->{Mode};
-    my $type  = $RT::Ticket::LINKTYPEMAP{$relation}->{Type};
+    my $mode  = $RT::Link::TYPEMAP{$relation}->{Mode};
+    my $type  = $RT::Link::TYPEMAP{$relation}->{Type};
     my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
     $clone->{$field} = $path . "&$field=$id";
 }
diff --git a/share/html/Elements/ShowRelationLabel b/share/html/Elements/ShowRelationLabel
index 9e6ea97..cf872c4 100644
--- a/share/html/Elements/ShowRelationLabel
+++ b/share/html/Elements/ShowRelationLabel
@@ -47,10 +47,10 @@
 %# END BPS TAGGED BLOCK }}}
 <a href="<%$SearchURL |n %>"><%$Label%></a>
 <%INIT>
-my $typemap = RT::Ticket->LINKTYPEMAP->{$Relation};
+my $typemap = $RT::Link::TYPEMAP{$Relation};
 my $search_mode = $typemap->{Mode};
 my $search_type = $typemap->{Type};
-my $search_relation = RT::Ticket->LINKDIRMAP->{$search_type}{$search_mode};
+my $search_relation = $RT::Link::DIRMAP{$search_type}->{$search_mode};
 
 my $Query = $search_relation . ' = ' . $id;
 my $SearchURL = RT->Config->Get('WebPath') . '/Search/Results.html?' . $m->comp('/Elements/QueryString', Query => $Query);

commit 5b833f649cd754e27bba672891d4ee2635e5d7ed
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Aug 17 16:04:21 2012 -0700

    Remove hardcoded duplicate types from CreateTickets.pm
    
    Provides a convenience method, RT::Link->DisplayTypes, for extracting
    the canonical directional link types from the (for now) public RT::Link
    data structures.
    
    The link data structures sorely want to be refactored even harder into
    an API.

diff --git a/lib/RT/Action/CreateTickets.pm b/lib/RT/Action/CreateTickets.pm
index 38028cb..d4d0d51 100644
--- a/lib/RT/Action/CreateTickets.pm
+++ b/lib/RT/Action/CreateTickets.pm
@@ -966,15 +966,7 @@ sub GetUpdateTemplate {
     $string .= "InitialPriority: " . $t->Priority . "\n";
     $string .= "FinalPriority: " . $t->FinalPriority . "\n";
 
-    foreach my $type ( sort keys %RT::Link::TYPEMAP ) {
-
-        # don't display duplicates
-        if (   $type eq "HasMember"
-            || $type eq "Members"
-            || $type eq "MemberOf" )
-        {
-            next;
-        }
+    foreach my $type ( RT::Link->DisplayTypes ) {
         $string .= "$type: ";
 
         my $mode   = $RT::Link::TYPEMAP{$type}->{Mode};
@@ -1044,15 +1036,7 @@ sub GetCreateTemplate {
     $string .= "InitialPriority: \n";
     $string .= "FinalPriority: \n";
 
-    foreach my $type ( keys %RT::Link::TYPEMAP ) {
-
-        # don't display duplicates
-        if (   $type eq "HasMember"
-            || $type eq 'Members'
-            || $type eq 'MemberOf' )
-        {
-            next;
-        }
+    foreach my $type ( RT::Link->DisplayTypes ) {
         $string .= "$type: \n";
     }
     return $string;
diff --git a/lib/RT/Link.pm b/lib/RT/Link.pm
index f1d0139..f9b2bd6 100644
--- a/lib/RT/Link.pm
+++ b/lib/RT/Link.pm
@@ -59,10 +59,6 @@
 This module should never be called directly by client code. it's an internal module which
 should only be accessed through exported APIs in Ticket other similar objects.
 
-=head1 METHODS
-
-
-
 =cut
 
 
@@ -78,20 +74,22 @@ use base 'RT::Record';
 sub Table {'Links'}
 use Carp;
 use RT::URI;
+use List::Util 'first';
+use List::MoreUtils 'uniq';
 
 # Helper tables for links mapping to make it easier
 # to build and parse links between objects.
 our %TYPEMAP = (
-    MemberOf        => { Type => 'MemberOf',    Mode => 'Target' },
-    Parents         => { Type => 'MemberOf',    Mode => 'Target' },
-    Members         => { Type => 'MemberOf',    Mode => 'Base'   },
-    Children        => { Type => 'MemberOf',    Mode => 'Base'   },
-    HasMember       => { Type => 'MemberOf',    Mode => 'Base'   },
-    RefersTo        => { Type => 'RefersTo',    Mode => 'Target' },
-    ReferredToBy    => { Type => 'RefersTo',    Mode => 'Base'   },
-    DependsOn       => { Type => 'DependsOn',   Mode => 'Target' },
-    DependedOnBy    => { Type => 'DependsOn',   Mode => 'Base'   },
-    MergedInto      => { Type => 'MergedInto',  Mode => 'Target' },
+    MemberOf        => { Type => 'MemberOf',    Mode => 'Target',   Display => 0 },
+    Parents         => { Type => 'MemberOf',    Mode => 'Target',   Display => 1 },
+    Members         => { Type => 'MemberOf',    Mode => 'Base',     Display => 0 },
+    Children        => { Type => 'MemberOf',    Mode => 'Base',     Display => 1 },
+    HasMember       => { Type => 'MemberOf',    Mode => 'Base',     Display => 0 },
+    RefersTo        => { Type => 'RefersTo',    Mode => 'Target',   Display => 1 },
+    ReferredToBy    => { Type => 'RefersTo',    Mode => 'Base',     Display => 1 },
+    DependsOn       => { Type => 'DependsOn',   Mode => 'Target',   Display => 1 },
+    DependedOnBy    => { Type => 'DependsOn',   Mode => 'Base',     Display => 1 },
+    MergedInto      => { Type => 'MergedInto',  Mode => 'Target',   Display => 1 },
 );
 our %DIRMAP = (
     MemberOf    => { Base => 'MemberOf',    Target => 'HasMember'    },
@@ -100,6 +98,40 @@ our %DIRMAP = (
     MergedInto  => { Base => 'MergedInto',  Target => 'MergedInto'   },
 );
 
+__PACKAGE__->_BuildDisplayAs;
+
+my %DISPLAY_AS;
+sub _BuildDisplayAs {
+    %DISPLAY_AS = ();
+    foreach my $in_db ( uniq map { $_->{Type} } values %TYPEMAP ) {
+        foreach my $mode (qw(Base Target)) {
+            $DISPLAY_AS{$in_db}{$mode} = first {
+                   $TYPEMAP{$_}{Display}
+                && $TYPEMAP{$_}{Type} eq $in_db
+                && $TYPEMAP{$_}{Mode} eq $mode
+            } keys %TYPEMAP;
+        }
+    }
+}
+
+=head1 CLASS METHODS
+
+=head2 DisplayTypes
+
+Returns a list of the standard link Types for display, including directional
+variants but not aliases.
+
+=cut
+
+sub DisplayTypes {
+    sort { $a cmp $b }
+    uniq
+    grep { defined }
+     map { values %$_ }
+  values %DISPLAY_AS
+}
+
+=head1 METHODS
 
 =head2 Create PARAMHASH
 
@@ -302,7 +334,6 @@ sub BaseObj {
   return $self->BaseURI->Object;
 }
 
-
 =head2 id
 
 Returns the current value of id.

commit 417945f7429832f4543cc0a3df9bb5721ffccf8d
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Aug 17 15:43:21 2012 -0700

    Support singular link types for ticket creation
    
    The singular forms are more intuitive than the plurals when dealing with
    a single link.

diff --git a/lib/RT/Link.pm b/lib/RT/Link.pm
index f9b2bd6..0f9fa9f 100644
--- a/lib/RT/Link.pm
+++ b/lib/RT/Link.pm
@@ -82,8 +82,11 @@ use List::MoreUtils 'uniq';
 our %TYPEMAP = (
     MemberOf        => { Type => 'MemberOf',    Mode => 'Target',   Display => 0 },
     Parents         => { Type => 'MemberOf',    Mode => 'Target',   Display => 1 },
+    Parent          => { Type => 'MemberOf',    Mode => 'Target',   Display => 0 },
     Members         => { Type => 'MemberOf',    Mode => 'Base',     Display => 0 },
+    Member          => { Type => 'MemberOf',    Mode => 'Base',     Display => 0 },
     Children        => { Type => 'MemberOf',    Mode => 'Base',     Display => 1 },
+    Child           => { Type => 'MemberOf',    Mode => 'Base',     Display => 0 },
     HasMember       => { Type => 'MemberOf',    Mode => 'Base',     Display => 0 },
     RefersTo        => { Type => 'RefersTo',    Mode => 'Target',   Display => 1 },
     ReferredToBy    => { Type => 'RefersTo',    Mode => 'Base',     Display => 1 },

commit eae7599d2282029137e62099b9e9c453ff715274
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Oct 18 17:16:51 2012 -0700

    Use our link maps instead of hardcoding link type pairs

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 8a01217..d42c3a3 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -1687,18 +1687,14 @@ sub CreateTicket {
     }
 
     # turn new link lists into arrays, and pass in the proper arguments
-    my %map = (
-        'new-DependsOn' => 'DependsOn',
-        'DependsOn-new' => 'DependedOnBy',
-        'new-MemberOf'  => 'Parents',
-        'MemberOf-new'  => 'Children',
-        'new-RefersTo'  => 'RefersTo',
-        'RefersTo-new'  => 'ReferredToBy',
-    );
-    foreach my $key ( keys %map ) {
-        next unless $ARGS{$key};
-        $create_args{ $map{$key} } = [ grep $_, split ' ', $ARGS{$key} ];
-
+    foreach my $type ( keys %RT::Link::DIRMAP ) {
+        for ([Base => "new-$type"], [Target => "$type-new"]) {
+            my ($direction, $key) = @$_;
+            next unless $ARGS{$key};
+            $create_args{ $RT::Link::DIRMAP{$type}->{$direction} } = [
+                grep $_, split ' ', $ARGS{$key}
+            ];
+        }
     }
 
     my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args);

commit 76a9d8dc934c014c86279b1c3b716a9b40609df7
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Oct 18 17:38:00 2012 -0700

    Refactor processing %ARGS for new links during record creation
    
    The inputs processed are generated by /Elements/EditLinks and may be
    used by object types other than tickets.  Refactoring into a function
    lets other objects re-use the same code.

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index d42c3a3..673ce42 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -1686,18 +1686,9 @@ sub CreateTicket {
         }
     }
 
-    # turn new link lists into arrays, and pass in the proper arguments
-    foreach my $type ( keys %RT::Link::DIRMAP ) {
-        for ([Base => "new-$type"], [Target => "$type-new"]) {
-            my ($direction, $key) = @$_;
-            next unless $ARGS{$key};
-            $create_args{ $RT::Link::DIRMAP{$type}->{$direction} } = [
-                grep $_, split ' ', $ARGS{$key}
-            ];
-        }
-    }
+    my %links = ProcessLinksForCreate( ARGSRef => \%ARGS );
 
-    my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args);
+    my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args, %links);
     unless ($id) {
         Abort($ErrMsg);
     }
@@ -2910,6 +2901,41 @@ sub ProcessRecordLinks {
     return (@results);
 }
 
+=head2 ProcessLinksForCreate
+
+Takes a hash with a single key, C<ARGSRef>, the value of which is a hashref to
+C<%ARGS>.
+
+Converts and returns submitted args in the form of C<new-LINKTYPE> and
+C<LINKTYPE-new> into their appropriate directional link types.  For example,
+C<new-DependsOn> becomes C<DependsOn> and C<DependsOn-new> becomes
+C<DependedOnBy>.  The incoming arg values are split on whitespace and
+normalized into arrayrefs before being returned.
+
+Primarily used by object creation pages for transforming incoming form inputs
+from F</Elements/EditLinks> into arguments appropriate for individual record
+Create methods.
+
+Returns a hashref in scalar context and a hash in list context.
+
+=cut
+
+sub ProcessLinksForCreate {
+    my %args = @_;
+    my %links;
+
+    foreach my $type ( keys %RT::Link::DIRMAP ) {
+        for ([Base => "new-$type"], [Target => "$type-new"]) {
+            my ($direction, $key) = @$_;
+            next unless $args{ARGSRef}->{$key};
+            $links{ $RT::Link::DIRMAP{$type}->{$direction} } = [
+                grep $_, split ' ', $args{ARGSRef}->{$key}
+            ];
+        }
+    }
+    return wantarray ? %links : \%links;
+}
+
 =head2 _UploadedFile ( $arg );
 
 Takes a CGI parameter name; if a file is uploaded under that name,

commit 1e54ce8b095d6d71ff6e5ad6500a9aef7beaeb05
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Oct 31 16:21:23 2012 -0700

    Only link relationship labels to ticket searches when editing ticket links
    
    EditLinks supports other object types than RT::Ticket and shouldn't
    display nonsensical links to ticket searches when the object in question
    isn't a ticket.
    
    Pass Object to ShowRelationLabel instead of id so ShowRelationLabel can
    make a more informed decision about display.

diff --git a/share/html/Elements/EditLinks b/share/html/Elements/EditLinks
index b3075a0..f3af89f 100755
--- a/share/html/Elements/EditLinks
+++ b/share/html/Elements/EditLinks
@@ -52,7 +52,7 @@
 
 <table>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Depends on'), Relation => 'DependsOn' &>:</td>
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Depends on'), Relation => 'DependsOn' &>:</td>
     <td class="value">
 % while (my $link = $Object->DependsOn->Next) {
       <input type="checkbox" class="checkbox" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
@@ -61,7 +61,7 @@
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:</td>
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:</td>
     <td class="value">
 % while (my $link = $Object->DependedOnBy->Next) {
       <input type="checkbox" class="checkbox" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
@@ -70,7 +70,7 @@
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Parents'), Relation => 'Parents' &>:</td>
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Parents'), Relation => 'Parents' &>:</td>
     <td class="value">
 % while (my $link = $Object->MemberOf->Next) {
       <input type="checkbox" class="checkbox" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
@@ -79,7 +79,7 @@
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Children'), Relation => 'Children' &>:</td>
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Children'), Relation => 'Children' &>:</td>
     <td class="value">
 % while (my $link = $Object->Members->Next) {
       <input type="checkbox" class="checkbox" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
@@ -88,7 +88,7 @@
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Refers to'), Relation => 'RefersTo' &>:</td>
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Refers to'), Relation => 'RefersTo' &>:</td>
     <td class="value">
 % while (my $link = $Object->RefersTo->Next) {
       <input type="checkbox" class="checkbox" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
@@ -97,7 +97,7 @@
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:</td>
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:</td>
     <td class="value">
 % while (my $link = $Object->ReferredToBy->Next) {
 % # Skip reminders
@@ -135,27 +135,27 @@
   </tr>
 % }
   <tr>
-    <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Depends on'), Relation => 'DependsOn' &>:</td>
+    <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Depends on'), Relation => 'DependsOn' &>:</td>
     <td class="entry"><input name="<%$id%>-DependsOn" /></td>
   </tr>
   <tr>
-    <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:</td>
+    <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:</td>
     <td class="entry"><input name="DependsOn-<%$id%>" /></td>
   </tr>
   <tr>
-    <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Parents'), Relation => 'Parents' &>:</td>
+    <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Parents'), Relation => 'Parents' &>:</td>
     <td class="entry"><input name="<%$id%>-MemberOf" /></td>
   </tr>
   <tr>
-    <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Children'), Relation => 'Children' &>:</td>
+    <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Children'), Relation => 'Children' &>:</td>
     <td class="entry"> <input name="MemberOf-<%$id%>" /></td>
   </tr>
   <tr>
-    <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Refers to'), Relation => 'RefersTo' &>:</td>
+    <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Refers to'), Relation => 'RefersTo' &>:</td>
     <td class="entry"><input name="<%$id%>-RefersTo" /></td>
   </tr>
   <tr>
-    <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:</td>
+    <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:</td>
     <td class="entry"> <input name="RefersTo-<%$id%>" /></td>
   </tr>
 % $m->callback( CallbackName => 'NewLink' );
diff --git a/share/html/Elements/ShowLinks b/share/html/Elements/ShowLinks
index 9a9536c..5e2ae1d 100755
--- a/share/html/Elements/ShowLinks
+++ b/share/html/Elements/ShowLinks
@@ -47,7 +47,7 @@
 %# END BPS TAGGED BLOCK }}}
 <table>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Depends on'), Relation => 'DependsOn' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Depends on'), Relation => 'DependsOn' &>:\
 % if ($can_create) {
         <span class="create">(<a href="<%$clone->{'DependsOn-new'}%>"><% loc('Create') %></a>)</span>
 % }
@@ -80,7 +80,7 @@ while ( my $link = $depends_on->Next ) {
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:\
 % if ($can_create) {
         <span class="create">(<a href="<%$clone->{'new-DependsOn'}%>"><% loc('Create') %></a>)</span>
 % }
@@ -94,7 +94,7 @@ while ( my $link = $depends_on->Next ) {
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Parents'), Relation => 'Parents' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Parents'), Relation => 'Parents' &>:\
 % if ($can_create) {
         <span class="create">(<a href="<%$clone->{'MemberOf-new'}%>"><% loc('Create') %></a>)</span>
 % }
@@ -102,7 +102,7 @@ while ( my $link = $depends_on->Next ) {
     <td class="value"><& /Ticket/Elements/ShowParents, Ticket => $Ticket &></td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Children'), Relation => 'Children' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Children'), Relation => 'Children' &>:\
 % if ($can_create) {
         <span class="create">(<a href="<%$clone->{'new-MemberOf'}%>"><% loc('Create') %></a>)</span>
 % }
@@ -110,7 +110,7 @@ while ( my $link = $depends_on->Next ) {
     <td class="value"><& /Ticket/Elements/ShowMembers, Ticket => $Ticket &></td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Refers to'), Relation => 'RefersTo' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Refers to'), Relation => 'RefersTo' &>:\
 % if ($can_create) {
         <span class="create">(<a href="<%$clone->{'RefersTo-new'}%>"><% loc('Create') %></a>)</span>
 % }
@@ -124,7 +124,7 @@ while ( my $link = $depends_on->Next ) {
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:\
 % if ($can_create) {
         <span class="create">(<a href="<%$clone->{'new-RefersTo'}%>"><% loc('Create') %></a>)</span>
 % }
diff --git a/share/html/Elements/ShowRelationLabel b/share/html/Elements/ShowRelationLabel
index cf872c4..430ac9c 100644
--- a/share/html/Elements/ShowRelationLabel
+++ b/share/html/Elements/ShowRelationLabel
@@ -45,18 +45,30 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<a href="<%$SearchURL |n %>"><%$Label%></a>
+% if ($SearchURL) {
+<a href="<% $SearchURL %>"><% $Label %></a>
+% } else {
+<% $Label %>
+% }
 <%INIT>
 my $typemap = $RT::Link::TYPEMAP{$Relation};
 my $search_mode = $typemap->{Mode};
 my $search_type = $typemap->{Type};
 my $search_relation = $RT::Link::DIRMAP{$search_type}->{$search_mode};
+my $SearchURL;
 
-my $Query = $search_relation . ' = ' . $id;
-my $SearchURL = RT->Config->Get('WebPath') . '/Search/Results.html?' . $m->comp('/Elements/QueryString', Query => $Query);
+if ($Object and $Object->id) {
+    my $id = $Object->id;
+
+    if ($Object->isa("RT::Ticket")) {
+        $SearchURL = RT->Config->Get('WebPath')
+            . '/Search/Results.html?'
+            . $m->comp('/Elements/QueryString', Query => "$search_relation = $id");
+    }
+}
 </%INIT>
 <%ARGS>
-$id
+$Object => undef
 $Label
 $Relation
 </%ARGS>

commit 6dc58c73b6c55f1232436874b55ec38bfe90689a
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Nov 20 13:46:53 2012 -0800

    Provide a callback for extensions to modify the relationship label link
    
    This lets any extensions using ShowLinks/EditLinks for objects other
    than tickets to link the labels as they see fit.

diff --git a/share/html/Elements/ShowRelationLabel b/share/html/Elements/ShowRelationLabel
index 430ac9c..6e6abda 100644
--- a/share/html/Elements/ShowRelationLabel
+++ b/share/html/Elements/ShowRelationLabel
@@ -66,6 +66,12 @@ if ($Object and $Object->id) {
             . $m->comp('/Elements/QueryString', Query => "$search_relation = $id");
     }
 }
+
+$m->callback(
+    CallbackName    => "ModifySearchURL",
+    SearchURL       => \$SearchURL,
+    ARGSRef         => \%ARGS,
+);
 </%INIT>
 <%ARGS>
 $Object => undef

commit cc946a9f16d120c371714b31947384620a156d02
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Nov 6 15:18:14 2012 -0800

    ShowLinks: Consolidate generation of Clone links and use RT::Link API

diff --git a/share/html/Elements/ShowLinks b/share/html/Elements/ShowLinks
index 5e2ae1d..3585afc 100755
--- a/share/html/Elements/ShowLinks
+++ b/share/html/Elements/ShowLinks
@@ -154,25 +154,20 @@ my $path
     . $id;
 my $can_create = $Ticket->QueueObj->CurrentUserHasRight('CreateTicket');
 
-for my $relation (
-    qw(RefersTo ReferredToBy)) {
+for my $relation (grep { $_ ne "MergedInto" } RT::Link->DisplayTypes) {
     my $mode  = $RT::Link::TYPEMAP{$relation}->{Mode};
     my $type  = $RT::Link::TYPEMAP{$relation}->{Type};
-    my $other = "Local" . $mode;
     my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
-    $clone->{$field} 
-        = $path . "&$field="
-        . join( '%20',
-        ( map { $_->$other() } @{ $Ticket->$relation->ItemsArrayRef } ), $id );
-}
+    my @copy  = ($id);
 
-for my $relation ( qw(MemberOf Members DependsOn DependedOnBy)) {
-    my $mode  = $RT::Link::TYPEMAP{$relation}->{Mode};
-    my $type  = $RT::Link::TYPEMAP{$relation}->{Type};
-    my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
-    $clone->{$field} = $path . "&$field=$id";
-}
+    # Canonicalized type captures both directions
+    if ($type eq "RefersTo") {
+        my $other = "Local" . $mode;
+        push @copy, map { $_->$other() } @{ $Ticket->$relation->ItemsArrayRef };
+    }
 
+    $clone->{$field} = "$path&$field=" . join('%20', @copy);
+}
 </%INIT>
 <%ARGS>
 $Ticket => undef

commit bb03d9260c13efc829def6a8133c0dcb69d05d7e
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Nov 6 15:28:45 2012 -0800

    ShowLinks: Construct clone links only if the current user can CreateTicket
    
    Instead of separating the CreateTicket check from the URL construction,
    simply skip constructing any clone URLs if the user can't CreateTicket
    and conditionalize the display of clone links on the presence of the
    constructed URLs themselves.
    
    A cleaner separation of concerns is useful in preparation for ShowLinks
    working on other objects other than Tickets.

diff --git a/share/html/Elements/ShowLinks b/share/html/Elements/ShowLinks
index 3585afc..73a8120 100755
--- a/share/html/Elements/ShowLinks
+++ b/share/html/Elements/ShowLinks
@@ -48,8 +48,8 @@
 <table>
   <tr>
     <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Depends on'), Relation => 'DependsOn' &>:\
-% if ($can_create) {
-        <span class="create">(<a href="<%$clone->{'DependsOn-new'}%>"><% loc('Create') %></a>)</span>
+% if (%clone) {
+        <span class="create">(<a href="<%$clone{'DependsOn-new'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value">
@@ -81,8 +81,8 @@ while ( my $link = $depends_on->Next ) {
   </tr>
   <tr>
     <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:\
-% if ($can_create) {
-        <span class="create">(<a href="<%$clone->{'new-DependsOn'}%>"><% loc('Create') %></a>)</span>
+% if (%clone) {
+        <span class="create">(<a href="<%$clone{'new-DependsOn'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value">
@@ -95,24 +95,24 @@ while ( my $link = $depends_on->Next ) {
   </tr>
   <tr>
     <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Parents'), Relation => 'Parents' &>:\
-% if ($can_create) {
-        <span class="create">(<a href="<%$clone->{'MemberOf-new'}%>"><% loc('Create') %></a>)</span>
+% if (%clone) {
+        <span class="create">(<a href="<%$clone{'MemberOf-new'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value"><& /Ticket/Elements/ShowParents, Ticket => $Ticket &></td>
   </tr>
   <tr>
     <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Children'), Relation => 'Children' &>:\
-% if ($can_create) {
-        <span class="create">(<a href="<%$clone->{'new-MemberOf'}%>"><% loc('Create') %></a>)</span>
+% if (%clone) {
+        <span class="create">(<a href="<%$clone{'new-MemberOf'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value"><& /Ticket/Elements/ShowMembers, Ticket => $Ticket &></td>
   </tr>
   <tr>
     <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Refers to'), Relation => 'RefersTo' &>:\
-% if ($can_create) {
-        <span class="create">(<a href="<%$clone->{'RefersTo-new'}%>"><% loc('Create') %></a>)</span>
+% if (%clone) {
+        <span class="create">(<a href="<%$clone{'RefersTo-new'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value">
@@ -125,8 +125,8 @@ while ( my $link = $depends_on->Next ) {
   </tr>
   <tr>
     <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:\
-% if ($can_create) {
-        <span class="create">(<a href="<%$clone->{'new-RefersTo'}%>"><% loc('Create') %></a>)</span>
+% if (%clone) {
+        <span class="create">(<a href="<%$clone{'new-RefersTo'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value">
@@ -142,31 +142,31 @@ while ( my $link = $depends_on->Next ) {
 % $m->callback( %ARGS );
 </table>
 <%INIT>
+my %clone;
 
-my $id = $Ticket->id;
+if ($Ticket->QueueObj->CurrentUserHasRight('CreateTicket')) {
+    my $id = $Ticket->id;
+    my $path
+        = RT->Config->Get('WebPath')
+        . '/Ticket/Create.html?Queue='
+        . $Ticket->Queue
+        . '&CloneTicket='
+        . $id;
 
-my $clone = {};
-my $path
-    = RT->Config->Get('WebPath')
-    . '/Ticket/Create.html?Queue='
-    . $Ticket->Queue
-    . '&CloneTicket='
-    . $id;
-my $can_create = $Ticket->QueueObj->CurrentUserHasRight('CreateTicket');
+    for my $relation (grep { $_ ne "MergedInto" } RT::Link->DisplayTypes) {
+        my $mode  = $RT::Link::TYPEMAP{$relation}->{Mode};
+        my $type  = $RT::Link::TYPEMAP{$relation}->{Type};
+        my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
+        my @copy  = ($id);
 
-for my $relation (grep { $_ ne "MergedInto" } RT::Link->DisplayTypes) {
-    my $mode  = $RT::Link::TYPEMAP{$relation}->{Mode};
-    my $type  = $RT::Link::TYPEMAP{$relation}->{Type};
-    my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
-    my @copy  = ($id);
+        # Canonicalized type captures both directions
+        if ($type eq "RefersTo") {
+            my $other = "Local" . $mode;
+            push @copy, map { $_->$other() } @{ $Ticket->$relation->ItemsArrayRef };
+        }
 
-    # Canonicalized type captures both directions
-    if ($type eq "RefersTo") {
-        my $other = "Local" . $mode;
-        push @copy, map { $_->$other() } @{ $Ticket->$relation->ItemsArrayRef };
+        $clone{$field} = "$path&$field=" . join('%20', @copy);
     }
-
-    $clone->{$field} = "$path&$field=" . join('%20', @copy);
 }
 </%INIT>
 <%ARGS>

commit e4edcd64fc2a32e8142520f220b33dcb34071f85
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Nov 7 14:22:53 2012 -0800

    ShowLinks: Work with any object that isa RT::Record, not just RT::Ticket
    
    One less not-actually-generic component in the top-level /Elements/ tree.
    
    There is still a lot of room for more refactoring, simplification, and
    consistency.

diff --git a/share/html/Elements/ShowLinks b/share/html/Elements/ShowLinks
index 73a8120..a3b2395 100755
--- a/share/html/Elements/ShowLinks
+++ b/share/html/Elements/ShowLinks
@@ -47,7 +47,7 @@
 %# END BPS TAGGED BLOCK }}}
 <table>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Depends on'), Relation => 'DependsOn' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Depends on'), Relation => 'DependsOn' &>:\
 % if (%clone) {
         <span class="create">(<a href="<%$clone{'DependsOn-new'}%>"><% loc('Create') %></a>)</span>
 % }
@@ -55,7 +55,7 @@
     <td class="value">
 <%PERL>
 my ( $depends_on, @active, @inactive, @not_tickets );
-$depends_on = $Ticket->DependsOn;
+$depends_on = $Object->DependsOn;
 
 while ( my $link = $depends_on->Next ) {
     my $target = $link->TargetObj;
@@ -80,58 +80,58 @@ while ( my $link = $depends_on->Next ) {
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:\
 % if (%clone) {
         <span class="create">(<a href="<%$clone{'new-DependsOn'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value">
 <ul>
-% while (my $Link = $Ticket->DependedOnBy->Next) {
+% while (my $Link = $Object->DependedOnBy->Next) {
 <li><& ShowLink, URI => $Link->BaseURI &></li>
 % }
 </ul>
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Parents'), Relation => 'Parents' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Parents'), Relation => 'Parents' &>:\
 % if (%clone) {
         <span class="create">(<a href="<%$clone{'MemberOf-new'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
-    <td class="value"><& /Ticket/Elements/ShowParents, Ticket => $Ticket &></td>
+    <td class="value"><& /Elements/ShowParents, Object => $Object &></td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Children'), Relation => 'Children' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Children'), Relation => 'Children' &>:\
 % if (%clone) {
         <span class="create">(<a href="<%$clone{'new-MemberOf'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
-    <td class="value"><& /Ticket/Elements/ShowMembers, Ticket => $Ticket &></td>
+    <td class="value"><& /Elements/ShowMembers, Object => $Object &></td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Refers to'), Relation => 'RefersTo' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Refers to'), Relation => 'RefersTo' &>:\
 % if (%clone) {
         <span class="create">(<a href="<%$clone{'RefersTo-new'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value">
 <ul>
-% while (my $Link = $Ticket->RefersTo->Next) {
+% while (my $Link = $Object->RefersTo->Next) {
 <li><& ShowLink, URI => $Link->TargetURI &></li>
 % }
 </ul>
     </td>
   </tr>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Ticket, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:\
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:\
 % if (%clone) {
         <span class="create">(<a href="<%$clone{'new-RefersTo'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value">
     <ul>
-% while (my $Link = $Ticket->ReferredToBy->Next) {
+% while (my $Link = $Object->ReferredToBy->Next) {
 % next if (UNIVERSAL::isa($Link->BaseObj, 'RT::Ticket')  && $Link->BaseObj->Type eq 'reminder');
 <li><& ShowLink, URI => $Link->BaseURI &></li>
 % }
@@ -144,12 +144,14 @@ while ( my $link = $depends_on->Next ) {
 <%INIT>
 my %clone;
 
-if ($Ticket->QueueObj->CurrentUserHasRight('CreateTicket')) {
-    my $id = $Ticket->id;
+if (    $Object->isa("RT::Ticket")
+    and $Object->QueueObj->CurrentUserHasRight('CreateTicket'))
+{
+    my $id = $Object->id;
     my $path
         = RT->Config->Get('WebPath')
         . '/Ticket/Create.html?Queue='
-        . $Ticket->Queue
+        . $Object->Queue
         . '&CloneTicket='
         . $id;
 
@@ -162,7 +164,7 @@ if ($Ticket->QueueObj->CurrentUserHasRight('CreateTicket')) {
         # Canonicalized type captures both directions
         if ($type eq "RefersTo") {
             my $other = "Local" . $mode;
-            push @copy, map { $_->$other() } @{ $Ticket->$relation->ItemsArrayRef };
+            push @copy, map { $_->$other() } @{ $Object->$relation->ItemsArrayRef };
         }
 
         $clone{$field} = "$path&$field=" . join('%20', @copy);
@@ -170,5 +172,5 @@ if ($Ticket->QueueObj->CurrentUserHasRight('CreateTicket')) {
 }
 </%INIT>
 <%ARGS>
-$Ticket => undef
+$Object
 </%ARGS>
diff --git a/share/html/Ticket/Elements/ShowMembers b/share/html/Elements/ShowMembers
old mode 100755
new mode 100644
similarity index 88%
rename from share/html/Ticket/Elements/ShowMembers
rename to share/html/Elements/ShowMembers
index 1ffbda2..e6da7db
--- a/share/html/Ticket/Elements/ShowMembers
+++ b/share/html/Elements/ShowMembers
@@ -47,10 +47,10 @@
 %# END BPS TAGGED BLOCK }}}
 <ul>
 % while (my $link = $members->Next) {
-<li><& /Elements/ShowLink, URI => $link->BaseURI &><br />
+<li><& ShowLink, URI => $link->BaseURI &>
 % next if $link->BaseObj and $checked->{$link->BaseObj->id};
 % if ($depth < 8) {
-<& /Ticket/Elements/ShowMembers, Ticket => $link->BaseObj, depth => ($depth+1), checked => $checked &> 
+<& ShowMembers, Object => $link->BaseObj, depth => ($depth+1), checked => $checked &>
 % }
 </li>
 % }
@@ -58,17 +58,17 @@
 
 <%INIT>
 
-return unless $Ticket;
-my $members = $Ticket->Members;
+return unless $Object;
+my $members = $Object->Members;
 return unless $members->Count;
 
-return if $checked->{$Ticket->id};
+return if $checked->{$Object->id};
 
-$checked->{$Ticket->id} = 1;
+$checked->{$Object->id} = 1;
 </%INIT>
 
 <%ARGS>
-$Ticket => undef
+$Object => undef
 $depth => 1
 $checked => {}
 </%ARGS>
diff --git a/share/html/Ticket/Elements/ShowParents b/share/html/Elements/ShowParents
similarity index 94%
rename from share/html/Ticket/Elements/ShowParents
rename to share/html/Elements/ShowParents
index 6dd3d0d..ddc2919 100644
--- a/share/html/Ticket/Elements/ShowParents
+++ b/share/html/Elements/ShowParents
@@ -47,17 +47,17 @@
 %# END BPS TAGGED BLOCK }}}
 <ul>
 % while (my $link = $member_of->Next) {
-<li><& /Elements/ShowLink, URI => $link->TargetURI &><br />
+<li><& ShowLink, URI => $link->TargetURI &></li>
 % }
 </ul>
 <%INIT>
 
-return unless $Ticket;
-my $member_of = $Ticket->MemberOf;
+return unless $Object;
+my $member_of = $Object->MemberOf;
 return unless $member_of->Count;
 
 </%INIT>
 
 <%ARGS>
-$Ticket => undef
+$Object => undef
 </%ARGS>
diff --git a/share/html/Ticket/Elements/ShowSummary b/share/html/Ticket/Elements/ShowSummary
index 421dd9a..0f0a0a6 100755
--- a/share/html/Ticket/Elements/ShowSummary
+++ b/share/html/Ticket/Elements/ShowSummary
@@ -92,7 +92,7 @@
         ($can_modify ? (title_href => RT->Config->Get('WebPath')."/Ticket/ModifyLinks.html?id=".$Ticket->Id) : ()),
         class => 'ticket-info-links',
         @extra,
-    &><& /Elements/ShowLinks, Ticket => $Ticket &></&>
+    &><& /Elements/ShowLinks, Object => $Ticket &></&>
 % $m->callback( %ARGS, CallbackName => 'RightColumn' );
   </td>
 </tr>
diff --git a/share/html/m/ticket/show b/share/html/m/ticket/show
index f6ffe88..fa6eb14 100644
--- a/share/html/m/ticket/show
+++ b/share/html/m/ticket/show
@@ -450,11 +450,11 @@ for my $link ( @{ $Ticket->DependsOn->ItemsArrayRef } ) {
   </div>
  <div class="entry">
     <div class="label"><% loc('Parents') %>:</div>
-    <div class="value"><& /Ticket/Elements/ShowParents, Ticket => $Ticket &></div>
+    <div class="value"><& /Elements/ShowParents, Object => $Ticket &></div>
   </div>
  <div class="entry">
     <div class="label"><% loc('Children')%>:</div>
-    <div class="value"><& /Ticket/Elements/ShowMembers, Ticket => $Ticket &></div>
+    <div class="value"><& /Elements/ShowMembers, Object => $Ticket &></div>
   </div>
  <div class="entry">
     <div class="label"><% loc('Refers to')%>:</div>

commit bbe7b408d18bc2ebcf13fb9bbcbd42b4714aea10
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Nov 7 19:28:14 2012 -0800

    ShowLinks: Refactor to remove a lot of repetition
    
    Removes the useless ShowParents element.  Moves most RT::Link loop and
    display logic into a separate component, ShowLinksOfType, where it's
    easier to make consistent changes.

diff --git a/share/html/Elements/ShowLinks b/share/html/Elements/ShowLinks
index a3b2395..06a984b 100755
--- a/share/html/Elements/ShowLinks
+++ b/share/html/Elements/ShowLinks
@@ -47,9 +47,9 @@
 %# END BPS TAGGED BLOCK }}}
 <table>
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Depends on'), Relation => 'DependsOn' &>:\
-% if (%clone) {
-        <span class="create">(<a href="<%$clone{'DependsOn-new'}%>"><% loc('Create') %></a>)</span>
+    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => $labels{'DependsOn'}, Relation => 'DependsOn' &>:\
+% if ($clone{'DependsOn'}) {
+        <span class="create">(<a href="<%$clone{'DependsOn'}%>"><% loc('Create') %></a>)</span>
 % }
     </td>
     <td class="value">
@@ -79,69 +79,34 @@ while ( my $link = $depends_on->Next ) {
 </ul>
     </td>
   </tr>
+
+% for my $type (grep { $_ ne "DependsOn" } @display) {
   <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:\
-% if (%clone) {
-        <span class="create">(<a href="<%$clone{'new-DependsOn'}%>"><% loc('Create') %></a>)</span>
-% }
-    </td>
-    <td class="value">
-<ul>
-% while (my $Link = $Object->DependedOnBy->Next) {
-<li><& ShowLink, URI => $Link->BaseURI &></li>
-% }
-</ul>
-    </td>
-  </tr>
-  <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Parents'), Relation => 'Parents' &>:\
-% if (%clone) {
-        <span class="create">(<a href="<%$clone{'MemberOf-new'}%>"><% loc('Create') %></a>)</span>
-% }
-    </td>
-    <td class="value"><& /Elements/ShowParents, Object => $Object &></td>
-  </tr>
-  <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Children'), Relation => 'Children' &>:\
-% if (%clone) {
-        <span class="create">(<a href="<%$clone{'new-MemberOf'}%>"><% loc('Create') %></a>)</span>
-% }
-    </td>
-    <td class="value"><& /Elements/ShowMembers, Object => $Object &></td>
-  </tr>
-  <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Refers to'), Relation => 'RefersTo' &>:\
-% if (%clone) {
-        <span class="create">(<a href="<%$clone{'RefersTo-new'}%>"><% loc('Create') %></a>)</span>
-% }
+    <td class="labeltop">
+      <& ShowRelationLabel, Object => $Object, Label => $labels{$type}, Relation => $type &>:\
+%     if ($clone{$type}) {
+        <span class="create">(<a href="<% $clone{$type} %>"><% loc('Create') %></a>)</span>
+%     }
     </td>
     <td class="value">
-<ul>
-% while (my $Link = $Object->RefersTo->Next) {
-<li><& ShowLink, URI => $Link->TargetURI &></li>
-% }
-</ul>
+      <& ShowLinksOfType, Object => $Object, Type => $type, Recurse => ($type eq 'Members') &>
     </td>
   </tr>
-  <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:\
-% if (%clone) {
-        <span class="create">(<a href="<%$clone{'new-RefersTo'}%>"><% loc('Create') %></a>)</span>
 % }
-    </td>
-    <td class="value">
-    <ul>
-% while (my $Link = $Object->ReferredToBy->Next) {
-% next if (UNIVERSAL::isa($Link->BaseObj, 'RT::Ticket')  && $Link->BaseObj->Type eq 'reminder');
-<li><& ShowLink, URI => $Link->BaseURI &></li>
-% }
-</ul>
-    </td>
-  </tr>
+
 % # Allow people to add more rows to the table
 % $m->callback( %ARGS );
 </table>
 <%INIT>
+my @display = qw(DependsOn DependedOnBy MemberOf Members RefersTo ReferredToBy);
+my %labels  = (
+    DependsOn       => loc('Depends on'),
+    DependedOnBy    => loc('Depended on by'),
+    MemberOf        => loc('Parents'),
+    Members         => loc('Children'),
+    RefersTo        => loc('Refers to'),
+    ReferredToBy    => loc('Referred to by'),
+);
 my %clone;
 
 if (    $Object->isa("RT::Ticket")
@@ -155,7 +120,7 @@ if (    $Object->isa("RT::Ticket")
         . '&CloneTicket='
         . $id;
 
-    for my $relation (grep { $_ ne "MergedInto" } RT::Link->DisplayTypes) {
+    for my $relation (@display) {
         my $mode  = $RT::Link::TYPEMAP{$relation}->{Mode};
         my $type  = $RT::Link::TYPEMAP{$relation}->{Type};
         my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
@@ -167,7 +132,7 @@ if (    $Object->isa("RT::Ticket")
             push @copy, map { $_->$other() } @{ $Object->$relation->ItemsArrayRef };
         }
 
-        $clone{$field} = "$path&$field=" . join('%20', @copy);
+        $clone{$relation} = "$path&$field=" . join('%20', @copy);
     }
 }
 </%INIT>
diff --git a/share/html/Elements/ShowMembers b/share/html/Elements/ShowLinksOfType
similarity index 68%
rename from share/html/Elements/ShowMembers
rename to share/html/Elements/ShowLinksOfType
index e6da7db..940d9f8 100644
--- a/share/html/Elements/ShowMembers
+++ b/share/html/Elements/ShowLinksOfType
@@ -46,29 +46,56 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <ul>
-% while (my $link = $members->Next) {
-<li><& ShowLink, URI => $link->BaseURI &>
-% next if $link->BaseObj and $checked->{$link->BaseObj->id};
-% if ($depth < 8) {
-<& ShowMembers, Object => $link->BaseObj, depth => ($depth+1), checked => $checked &>
-% }
+<%perl>
+while (my $link = $links->Next) {
+    next if $Type eq "ReferredToBy"
+        and UNIVERSAL::isa($link->BaseObj, 'RT::Ticket')
+        and $link->BaseObj->Type eq 'reminder';
+
+    my $ModeURI = "${mode}URI";
+</%perl>
+<li><& ShowLink, URI => $link->$ModeURI &>
+<%perl>
+    next unless $Recurse;
+
+    my $ModeObj = "${mode}Obj";
+    my $ToObj   = $link->$ModeObj;
+
+    next if $ToObj and $checked->{$ToObj->id};
+    if ($depth <= $MaxDepth) {
+</%perl>
+<& ShowLinksOfType, %ARGS, Object => $ToObj, depth => ($depth + 1), checked => $checked &>
+%   }
 </li>
 % }
 </ul>
-
 <%INIT>
-
 return unless $Object;
-my $members = $Object->Members;
-return unless $members->Count;
+
+unless ($RT::Link::TYPEMAP{$Type}) {
+    RT->Logger->error("Unknown link Type '$ARGS{Type}'");
+    return;
+}
+
+unless ($Object->can($Type)) {
+    RT->Logger->error("Don't know how to fetch links of '$Type' for object '$Object'");
+    return;
+}
+
+my $links = $Object->$Type;
+return unless $links->Count;
 
 return if $checked->{$Object->id};
 
 $checked->{$Object->id} = 1;
-</%INIT>
 
+my $mode = $RT::Link::TYPEMAP{$Type}->{'Mode'};
+</%INIT>
 <%ARGS>
-$Object => undef
-$depth => 1
-$checked => {}
+$Object     => undef
+$Type
+$Recurse    => 0
+$MaxDepth   => 7
+$depth      => 1
+$checked    => {}
 </%ARGS>
diff --git a/share/html/Elements/ShowParents b/share/html/Elements/ShowParents
deleted file mode 100644
index ddc2919..0000000
--- a/share/html/Elements/ShowParents
+++ /dev/null
@@ -1,63 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<ul>
-% while (my $link = $member_of->Next) {
-<li><& ShowLink, URI => $link->TargetURI &></li>
-% }
-</ul>
-<%INIT>
-
-return unless $Object;
-my $member_of = $Object->MemberOf;
-return unless $member_of->Count;
-
-</%INIT>
-
-<%ARGS>
-$Object => undef
-</%ARGS>
diff --git a/share/html/m/ticket/show b/share/html/m/ticket/show
index fa6eb14..db5f93a 100644
--- a/share/html/m/ticket/show
+++ b/share/html/m/ticket/show
@@ -450,11 +450,11 @@ for my $link ( @{ $Ticket->DependsOn->ItemsArrayRef } ) {
   </div>
  <div class="entry">
     <div class="label"><% loc('Parents') %>:</div>
-    <div class="value"><& /Elements/ShowParents, Object => $Ticket &></div>
+    <div class="value"><& /Elements/ShowLinksOfType, Object => $Ticket, Type => 'MemberOf' &></div>
   </div>
  <div class="entry">
     <div class="label"><% loc('Children')%>:</div>
-    <div class="value"><& /Elements/ShowMembers, Object => $Ticket &></div>
+    <div class="value"><& /Elements/ShowLinksOfType, Object => $Ticket, Type => 'Members' &></div>
   </div>
  <div class="entry">
     <div class="label"><% loc('Refers to')%>:</div>

commit 0538e83f2156db7bd774505ea5c1bcbe6e55b2f3
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Nov 7 19:47:33 2012 -0800

    ShowLinksOfType: Add a callback to allow changing $Recurse and $MaxDepth
    
    It is sometimes useful to recurse on link types other than just
    Children.

diff --git a/share/html/Elements/ShowLinksOfType b/share/html/Elements/ShowLinksOfType
index 940d9f8..12e9559 100644
--- a/share/html/Elements/ShowLinksOfType
+++ b/share/html/Elements/ShowLinksOfType
@@ -90,6 +90,15 @@ return if $checked->{$Object->id};
 $checked->{$Object->id} = 1;
 
 my $mode = $RT::Link::TYPEMAP{$Type}->{'Mode'};
+
+$m->callback(
+    CallbackName => "Init",
+    ARGSRef      => \%ARGS,
+    Object       => $Object,
+    $Type        => $Type,
+    $Recurse     => \$Recurse,
+    $MaxDepth    => \$MaxDepth,
+);
 </%INIT>
 <%ARGS>
 $Object     => undef

commit 8430d89d0fb907ff587322da7f0a1682f7da306b
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Nov 7 20:00:08 2012 -0800

    ShowLinks: Sort links in all relationship types as not tickets, active, or inactive
    
    It's useful to see inactive linked tickets last as well as to have an
    overall consistent ordering.

diff --git a/share/html/Elements/ShowLinks b/share/html/Elements/ShowLinks
index 06a984b..a81dca9 100755
--- a/share/html/Elements/ShowLinks
+++ b/share/html/Elements/ShowLinks
@@ -46,41 +46,7 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <table>
-  <tr>
-    <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => $labels{'DependsOn'}, Relation => 'DependsOn' &>:\
-% if ($clone{'DependsOn'}) {
-        <span class="create">(<a href="<%$clone{'DependsOn'}%>"><% loc('Create') %></a>)</span>
-% }
-    </td>
-    <td class="value">
-<%PERL>
-my ( $depends_on, @active, @inactive, @not_tickets );
-$depends_on = $Object->DependsOn;
-
-while ( my $link = $depends_on->Next ) {
-    my $target = $link->TargetObj;
-    if ( $target && $target->isa('RT::Ticket') ) {
-        if ( $target->QueueObj->IsInactiveStatus( $target->Status ) ) {
-            push( @inactive, $link->TargetURI );
-        }
-        else {
-            push( @active, $link->TargetURI );
-        }
-    }
-    else {
-        push( @not_tickets, $link->TargetURI );
-    }
-}
-</%PERL>
-<ul>
-% for my $Link (@not_tickets, @active, @inactive) {
-<li><& ShowLink, URI => $Link &></li>
-% }
-</ul>
-    </td>
-  </tr>
-
-% for my $type (grep { $_ ne "DependsOn" } @display) {
+% for my $type (@display) {
   <tr>
     <td class="labeltop">
       <& ShowRelationLabel, Object => $Object, Label => $labels{$type}, Relation => $type &>:\
diff --git a/share/html/Elements/ShowLinksOfType b/share/html/Elements/ShowLinksOfType
index 12e9559..7ad08d2 100644
--- a/share/html/Elements/ShowLinksOfType
+++ b/share/html/Elements/ShowLinksOfType
@@ -46,22 +46,14 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <ul>
-<%perl>
-while (my $link = $links->Next) {
-    next if $Type eq "ReferredToBy"
-        and UNIVERSAL::isa($link->BaseObj, 'RT::Ticket')
-        and $link->BaseObj->Type eq 'reminder';
-
-    my $ModeURI = "${mode}URI";
-</%perl>
+% for my $link (@not_tickets, @active, @inactive) {
 <li><& ShowLink, URI => $link->$ModeURI &>
 <%perl>
     next unless $Recurse;
 
-    my $ModeObj = "${mode}Obj";
-    my $ToObj   = $link->$ModeObj;
-
+    my $ToObj = $link->$ModeObj;
     next if $ToObj and $checked->{$ToObj->id};
+
     if ($depth <= $MaxDepth) {
 </%perl>
 <& ShowLinksOfType, %ARGS, Object => $ToObj, depth => ($depth + 1), checked => $checked &>
@@ -89,7 +81,29 @@ return if $checked->{$Object->id};
 
 $checked->{$Object->id} = 1;
 
-my $mode = $RT::Link::TYPEMAP{$Type}->{'Mode'};
+my $mode    = $RT::Link::TYPEMAP{$Type}->{'Mode'};
+my $ModeURI = "${mode}URI";
+my $ModeObj = "${mode}Obj";
+
+# Filter and bucket
+my (@active, @inactive, @not_tickets);
+while (my $link = $links->Next) {
+    my $ToObj = $link->$ModeObj;
+    if ($ToObj and $ToObj->isa('RT::Ticket')) {
+        next if $Type eq "ReferredToBy"
+            and $ToObj->Type eq 'reminder';
+
+        if ( $ToObj->QueueObj->IsInactiveStatus( $ToObj->Status ) ) {
+            push @inactive, $link;
+        }
+        else {
+            push @active, $link;
+        }
+    }
+    else {
+        push @not_tickets, $link;
+    }
+}
 
 $m->callback(
     CallbackName => "Init",
@@ -98,6 +112,9 @@ $m->callback(
     $Type        => $Type,
     $Recurse     => \$Recurse,
     $MaxDepth    => \$MaxDepth,
+    active       => \@active,
+    inactive     => \@inactive,
+    not_tickets  => \@not_tickets,
 );
 </%INIT>
 <%ARGS>

commit 8fce29da0d28ed9ccbfff21f5b94b5c61e980364
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Nov 20 13:41:41 2012 -0800

    ShowLinks: Not all RefersTo links have a LocalTarget; weed out undefined values

diff --git a/share/html/Elements/ShowLinks b/share/html/Elements/ShowLinks
index a81dca9..1f3501a 100755
--- a/share/html/Elements/ShowLinks
+++ b/share/html/Elements/ShowLinks
@@ -98,7 +98,7 @@ if (    $Object->isa("RT::Ticket")
             push @copy, map { $_->$other() } @{ $Object->$relation->ItemsArrayRef };
         }
 
-        $clone{$relation} = "$path&$field=" . join('%20', @copy);
+        $clone{$relation} = "$path&$field=" . join('%20', grep { $_ } @copy);
     }
 }
 </%INIT>

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


More information about the Rt-commit mailing list