[Rt-commit] rt branch, 4.2/lifecycle-status-generalization, created. rt-4.1.5-47-gcb4997a

Alex Vandiver alexmv at bestpractical.com
Fri Dec 14 16:10:03 EST 2012


The branch, 4.2/lifecycle-status-generalization has been created
        at  cb4997a4bd79677afe21ccdc99e9e0c8e544e83d (commit)

- Log -----------------------------------------------------------------
commit 6cad570e1f1e376784103438934f3b9d05350c29
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Dec 7 15:08:28 2012 -0500

    Factor out common code for setting Started and Resolved

diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index 7faa223..c643a2d 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -1389,36 +1389,10 @@ sub SetQueue {
             return ( 0, $self->loc("Couldn't load copy of ticket #[_1].", $self->Id) );
         }
 
-        my $now = RT::Date->new( $self->CurrentUser );
-        $now->SetToNow;
-
-        my $old_status = $clone->Status;
-
-        #If we're changing the status from initial in old to not intial in new,
-        # record that we've started
-        if ( $old_lifecycle->IsInitial($old_status) && !$new_lifecycle->IsInitial($new_status)  && $clone->StartedObj->Unix == 0 ) {
-            #Set the Started time to "now"
-            $clone->_Set(
-                Field             => 'Started',
-                Value             => $now->ISO,
-                RecordTransaction => 0
-            );
-        }
-
-        #When we close a ticket, set the 'Resolved' attribute to now.
-        # It's misnamed, but that's just historical.
-        if ( $new_lifecycle->IsInactive($new_status) ) {
-            $clone->_Set(
-                Field             => 'Resolved',
-                Value             => $now->ISO,
-                RecordTransaction => 0,
-            );
-        }
-
-        #Actually update the status
-        my ($val, $msg)= $clone->_Set(
-            Field             => 'Status',
-            Value             => $new_status,
+        my ($val, $msg) = $clone->_SetStatus(
+            Lifecycle         => $old_lifecycle,
+            NewLifecycle      => $new_lifecycle,
+            Status            => $new_status,
             RecordTransaction => 0,
         );
         $RT::Logger->error( 'Status change failed on queue change: '. $msg )
@@ -2701,15 +2675,37 @@ sub SetStatus {
         return ( 0, $self->loc('That ticket has unresolved dependencies') );
     }
 
+    return $self->_SetStatus(
+        Status     => $args{Status},
+        SetStarted => $args{SetStarted},
+    );
+}
+
+sub _SetStatus {
+    my $self = shift;
+    my %args = (
+        Status => undef,
+        SetStarted => 1,
+        RecordTransaction => 1,
+        Lifecycle => $self->QueueObj->Lifecycle,
+        @_,
+    );
+    $args{NewLifecycle} ||= $args{Lifecycle};
+
     my $now = RT::Date->new( $self->CurrentUser );
     $now->SetToNow();
 
     my $raw_started = RT::Date->new(RT->SystemUser);
     $raw_started->Set(Format => 'ISO', Value => $self->__Value('Started'));
 
-    #If we're changing the status from new, record that we've started
-    if ( $args{SetStarted} && $lifecycle->IsInitial($old) && !$lifecycle->IsInitial($new) && !$raw_started->Unix) {
-        #Set the Started time to "now"
+    my $old = $self->__Value('Status');
+
+    # If we're changing the status from new, record that we've started
+    if ( $args{SetStarted}
+             && $args{Lifecycle}->IsInitial($old)
+             && !$args{NewLifecycle}->IsInitial($args{Status})
+             && !$raw_started->Unix) {
+        # Set the Started time to "now"
         $self->_Set(
             Field             => 'Started',
             Value             => $now->ISO,
@@ -2717,9 +2713,9 @@ sub SetStatus {
         );
     }
 
-    #When we close a ticket, set the 'Resolved' attribute to now.
+    # When we close a ticket, set the 'Resolved' attribute to now.
     # It's misnamed, but that's just historical.
-    if ( $lifecycle->IsInactive($new) ) {
+    if ( $args{NewLifecycle}->IsInactive($args{Status}) ) {
         $self->_Set(
             Field             => 'Resolved',
             Value             => $now->ISO,
@@ -2727,13 +2723,14 @@ sub SetStatus {
         );
     }
 
-    #Actually update the status
+    # Actually update the status
     my ($val, $msg)= $self->_Set(
         Field           => 'Status',
         Value           => $args{Status},
         TimeTaken       => 0,
         CheckACL        => 0,
         TransactionType => 'Status',
+        RecordTransaction => $args{RecordTransaction},
     );
     return ($val, $msg);
 }

commit 9afdee9389c15abd15a90547ca3ffe0565ee2828
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Dec 7 12:53:00 2012 -0500

    Allow lifecycles to be applied to different objects types
    
    Refactor ticket-specific methods of tickets to RT::Lifecycle::Ticket,
    and re-bless RT::Lifecycle objects on creation to the appropriate
    sublclass based on 'type'.  This opens the possibility of objects that
    are not tickets using lifecycles for their 'Status' columns.

diff --git a/lib/RT/Lifecycle.pm b/lib/RT/Lifecycle.pm
index 056599e..4aa3f49 100644
--- a/lib/RT/Lifecycle.pm
+++ b/lib/RT/Lifecycle.pm
@@ -141,6 +141,10 @@ sub Load {
     $self->{'name'} = $name;
     $self->{'data'} = $LIFECYCLES_CACHE{ $name };
 
+    my $type = $self->{'data'}{'type'} || 'ticket';
+    $type = "RT::Lifecycle::".ucfirst($type);
+    bless $self, $type if $type->require;
+
     return $self;
 }
 
@@ -152,34 +156,22 @@ Returns sorted list of the lifecycles' names.
 
 sub List {
     my $self = shift;
+    my $for = shift || 'ticket';
 
     $self->FillCache unless keys %LIFECYCLES_CACHE;
 
-    return sort grep length && $_ ne '__maps__', keys %LIFECYCLES_CACHE;
+    return sort grep {$LIFECYCLES_CACHE{$_}{type} eq $for}
+        grep length && $_ ne '__maps__', keys %LIFECYCLES_CACHE;
 }
 
 =head2 Name
 
-Returns name of the laoded lifecycle.
+Returns name of the loaded lifecycle.
 
 =cut
 
 sub Name { return $_[0]->{'name'} }
 
-=head2 Queues
-
-Returns L<RT::Queues> collection with queues that use this lifecycle.
-
-=cut
-
-sub Queues {
-    my $self = shift;
-    require RT::Queues;
-    my $queues = RT::Queues->new( RT->SystemUser );
-    $queues->Limit( FIELD => 'Lifecycle', VALUE => $self->Name );
-    return $queues;
-}
-
 =head2 Getting statuses and validating.
 
 Methods to get statuses in different sets or validating them.
@@ -354,41 +346,6 @@ sub DefaultOnCreate {
     return $self->DefaultStatus('on_create');
 }
 
-
-=head3 DefaultOnMerge
-
-Returns the status that should be used when tickets
-are merged.
-
-=cut
-
-sub DefaultOnMerge {
-    my $self = shift;
-    return $self->DefaultStatus('on_merge');
-}
-
-=head3 ReminderStatusOnOpen
-
-Returns the status that should be used when reminders are opened.
-
-=cut
-
-sub ReminderStatusOnOpen {
-    my $self = shift;
-    return $self->DefaultStatus('reminder_on_open') || 'open';
-}
-
-=head3 ReminderStatusOnResolve
-
-Returns the status that should be used when reminders are resolved.
-
-=cut
-
-sub ReminderStatusOnResolve {
-    my $self = shift;
-    return $self->DefaultStatus('reminder_on_resolve') || 'resolved';
-}
-
 =head2 Transitions, rights, labels and actions.
 
 =head3 Transitions
@@ -641,6 +598,10 @@ sub FillCache {
     %LIFECYCLES_CACHE = %LIFECYCLES = %$map;
     $_ = { %$_ } foreach values %LIFECYCLES_CACHE;
 
+    for my $lifecycles (values %LIFECYCLES_CACHE) {
+        $lifecycles->{type} ||= 'ticket';
+    }
+
     my %all = (
         '' => [],
         initial => [],
diff --git a/lib/RT/Lifecycle/Ticket.pm b/lib/RT/Lifecycle/Ticket.pm
new file mode 100644
index 0000000..1cd8e7c
--- /dev/null
+++ b/lib/RT/Lifecycle/Ticket.pm
@@ -0,0 +1,104 @@
+# 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 }}}
+
+use strict;
+use warnings;
+
+package RT::Lifecycle::Ticket;
+
+use base qw(RT::Lifecycle);
+
+=head2 Queues
+
+Returns L<RT::Queues> collection with queues that use this lifecycle.
+
+=cut
+
+sub Queues {
+    my $self = shift;
+    require RT::Queues;
+    my $queues = RT::Queues->new( RT->SystemUser );
+    $queues->Limit( FIELD => 'Lifecycle', VALUE => $self->Name );
+    return $queues;
+}
+
+=head3 DefaultOnMerge
+
+Returns the status that should be used when tickets
+are merged.
+
+=cut
+
+sub DefaultOnMerge {
+    my $self = shift;
+    return $self->DefaultStatus('on_merge');
+}
+
+=head3 ReminderStatusOnOpen
+
+Returns the status that should be used when reminders are opened.
+
+=cut
+
+sub ReminderStatusOnOpen {
+    my $self = shift;
+    return $self->DefaultStatus('reminder_on_open') || 'open';
+}
+
+=head3 ReminderStatusOnResolve
+
+Returns the status that should be used when reminders are resolved.
+
+=cut
+
+sub ReminderStatusOnResolve {
+    my $self = shift;
+    return $self->DefaultStatus('reminder_on_resolve') || 'resolved';
+}
+
+1;

commit 104fed628ba0eb19c54832099408b38ab4da76d5
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Dec 7 21:44:07 2012 -0500

    Minor cleanup of lifecycle tests

diff --git a/t/lifecycles/basics.t b/t/lifecycles/basics.t
index 554c95a..b4828f7 100644
--- a/t/lifecycles/basics.t
+++ b/t/lifecycles/basics.t
@@ -1,7 +1,5 @@
-
 use strict;
 use warnings;
-use Data::Dumper;
 
 BEGIN {require  't/lifecycles/utils.pl'};
 
@@ -244,3 +242,5 @@ diag "'!inactive -> inactive' actions are shown even if ticket has unresolved de
     );
 }
 
+undef $m;
+done_testing;
diff --git a/t/lifecycles/dates.t b/t/lifecycles/dates.t
index 4f613f8..80c24fc 100644
--- a/t/lifecycles/dates.t
+++ b/t/lifecycles/dates.t
@@ -1,7 +1,5 @@
-
 use strict;
 use warnings;
-use Data::Dumper;
 
 BEGIN {require 't/lifecycles/utils.pl'};
 
@@ -23,9 +21,6 @@ my $tstatus = sub {
     return $ticket->Status;
 };
 
-my ($baseurl, $m) = RT::Test->started_ok;
-ok $m->login, 'logged in';
-
 diag "check basic API";
 {
     my $schema = $general->Lifecycle;
@@ -314,3 +309,5 @@ diag "check date changes on moving a ticket";
     ok $ticket->StartedObj->Unix > 0, 'started is set';
     ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
 }
+
+done_testing;
diff --git a/t/lifecycles/moving.t b/t/lifecycles/moving.t
index 5f184e2..8a03e3e 100644
--- a/t/lifecycles/moving.t
+++ b/t/lifecycles/moving.t
@@ -1,7 +1,5 @@
-
 use strict;
 use warnings;
-use Data::Dumper;
 
 BEGIN {require 't/lifecycles/utils.pl'};
 
@@ -94,3 +92,5 @@ diag "one way map doesn't work backwards";
     is $ticket->Queue, $delivery->id, 'queue is steal the same';
     is $ticket->Status, 'ordered', 'status is steal the same';
 }
+
+done_testing;
diff --git a/t/lifecycles/unresolved-deps.t b/t/lifecycles/unresolved-deps.t
index 02c1942..5da4b8f 100644
--- a/t/lifecycles/unresolved-deps.t
+++ b/t/lifecycles/unresolved-deps.t
@@ -1,8 +1,6 @@
 use strict;
 use warnings;
-use Data::Dumper;
 
-use Test::More tests => 15 + 1; # plus one for warnings check
 BEGIN {require  't/lifecycles/utils.pl'};
 
 my $general = RT::Test->load_or_create_queue(
@@ -41,3 +39,5 @@ ok $m->login, 'logged in';
     );
 }
 
+undef $m;
+done_testing;
diff --git a/t/lifecycles/utils.pl b/t/lifecycles/utils.pl
index 3813df3..ec3289f 100644
--- a/t/lifecycles/utils.pl
+++ b/t/lifecycles/utils.pl
@@ -67,6 +67,6 @@ Set(\%Lifecycles,
 END
 }
 
-use RT::Test config => $config;
+use RT::Test config => $config, tests => undef;
 
 1;

commit cc502677fae283818023d94694a9c9d1b5797215
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Dec 7 21:56:48 2012 -0500

    Split lifecycle status aggregation by type
    
    This ensures that as lifecycles are applied to more than just tickets,
    their aggregated statuses (as determined from ->Load( '' )->Valid) only
    show statuses from lifecycles that could apply to that type of object.

diff --git a/lib/RT/Condition/StatusChange.pm b/lib/RT/Condition/StatusChange.pm
index 667ccee..1de7a8c 100644
--- a/lib/RT/Condition/StatusChange.pm
+++ b/lib/RT/Condition/StatusChange.pm
@@ -114,7 +114,7 @@ sub IsApplicable {
     }
     else {
         $RT::Logger->error("Argument '$argument' is incorrect.")
-            unless RT::Lifecycle->Load('')->IsValid( $argument );
+            unless RT::Lifecycle->Load(Type => 'ticket')->IsValid( $argument );
         return 0;
     }
 
diff --git a/lib/RT/Lifecycle.pm b/lib/RT/Lifecycle.pm
index 4aa3f49..b533746 100644
--- a/lib/RT/Lifecycle.pm
+++ b/lib/RT/Lifecycle.pm
@@ -54,16 +54,11 @@ package RT::Lifecycle;
 
 our %LIFECYCLES;
 our %LIFECYCLES_CACHE;
+our %LIFECYCLES_TYPES;
 __PACKAGE__->RegisterRights;
 
 # cache structure:
 #    {
-#        '' => { # all valid statuses
-#            '' => [...],
-#            initial => [...],
-#            active => [...],
-#            inactive => [...],
-#        },
 #        lifecycle_x => {
 #            '' => [...], # all valid in lifecycle
 #            initial => [...],
@@ -119,31 +114,50 @@ sub new {
     return $self;
 }
 
-=head2 Load
+=head2 Load Name => I<NAME>, Type => I<TYPE>
 
-Takes a name of the lifecycle and loads it. If name is empty or undefined then
-loads the global lifecycle with statuses from all named lifecycles.
+Takes a name of the lifecycle and loads it. If only a Type is provided,
+loads the global lifecycle with statuses from all named lifecycles of
+that type.
 
 Can be called as class method, returns a new object, for example:
 
-    my $lifecycle = RT::Lifecycle->Load('default');
+    my $lifecycle = RT::Lifecycle->Load( Name => 'default');
+
+Returns an object which may be a subclass of L<RT::Lifecycle>
+(L<RT::Lifecycle::Ticket>, for example) depending on the type of the
+lifecycle in question.
 
 =cut
 
 sub Load {
     my $self = shift;
-    my $name = shift || '';
-    return $self->new->Load( $name, @_ )
+    return $self->new->Load( @_ )
         unless ref $self;
 
-    return unless exists $LIFECYCLES_CACHE{ $name };
+    unshift @_, Type => "ticket", "Name"
+        if @_ % 2;
 
-    $self->{'name'} = $name;
-    $self->{'data'} = $LIFECYCLES_CACHE{ $name };
+    my %args = (
+        Type => "ticket",
+        Name => '',
+        @_,
+    );
+
+    if (defined $args{Name} and exists $LIFECYCLES_CACHE{ $args{Name} }) {
+        $self->{'name'} = $args{Name};
+        $self->{'data'} = $LIFECYCLES_CACHE{ $args{Name} };
 
-    my $type = $self->{'data'}{'type'} || 'ticket';
-    $type = "RT::Lifecycle::".ucfirst($type);
-    bless $self, $type if $type->require;
+        my $found_type = $self->{'data'}{'type'};
+        warn "Found type of $found_type ne $args{Type}" if $found_type ne $args{Type};
+    } elsif (not $args{Name} and exists $LIFECYCLES_TYPES{ $args{Type} }) {
+        $self->{'data'} = $LIFECYCLES_TYPES{ $args{Type} };
+    } else {
+        return undef;
+    }
+
+    my $class = "RT::Lifecycle::".ucfirst($args{Type});
+    bless $self, $class if $class->require;
 
     return $self;
 }
@@ -161,7 +175,7 @@ sub List {
     $self->FillCache unless keys %LIFECYCLES_CACHE;
 
     return sort grep {$LIFECYCLES_CACHE{$_}{type} eq $for}
-        grep length && $_ ne '__maps__', keys %LIFECYCLES_CACHE;
+        grep $_ ne '__maps__', keys %LIFECYCLES_CACHE;
 }
 
 =head2 Name
@@ -518,7 +532,8 @@ move map from this cycle to provided.
 sub MoveMap {
     my $from = shift; # self
     my $to = shift;
-    $to = RT::Lifecycle->Load( $to ) unless ref $to;
+    my $type = $from->{'data'}{'type'};
+    $to = RT::Lifecycle->Load( Name => $to, Type => $type ) unless ref $to;
     return $LIFECYCLES{'__maps__'}{ $from->Name .' -> '. $to->Name } || {};
 }
 
@@ -546,13 +561,14 @@ move maps.
 
 sub NoMoveMaps {
     my $self = shift;
-    my @list = $self->List;
+    my $type = $self->{'data'}{'type'};
+    my @list = $self->List( $type );
     my @res;
     foreach my $from ( @list ) {
         foreach my $to ( @list ) {
             next if $from eq $to;
             push @res, $from, $to
-                unless RT::Lifecycle->Load( $from )->HasMoveMap( $to );
+                unless RT::Lifecycle->Load( Name => $from, Type => $type )->HasMoveMap( $to );
         }
     }
     return @res;
@@ -573,7 +589,7 @@ sub ForLocalization {
 
     my @res = ();
 
-    push @res, @{ $LIFECYCLES_CACHE{''}{''} || [] };
+    push @res, @{$_->{''}} for values %LIFECYCLES_TYPES;
     foreach my $lifecycle ( values %LIFECYCLES ) {
         push @res,
             grep defined && length,
@@ -598,21 +614,21 @@ sub FillCache {
     %LIFECYCLES_CACHE = %LIFECYCLES = %$map;
     $_ = { %$_ } foreach values %LIFECYCLES_CACHE;
 
-    for my $lifecycles (values %LIFECYCLES_CACHE) {
-        $lifecycles->{type} ||= 'ticket';
-    }
+    for my $lifecycle ( values %LIFECYCLES_CACHE ) {
+        my $type = $lifecycle->{type} ||= 'ticket';
+        $LIFECYCLES_TYPES{$type} ||= {
+            '' => [],
+            initial => [],
+            active => [],
+            inactive => [],
+            actions => [],
+        };
 
-    my %all = (
-        '' => [],
-        initial => [],
-        active => [],
-        inactive => [],
-    );
-    foreach my $lifecycle ( values %LIFECYCLES_CACHE ) {
         my @res;
-        foreach my $type ( qw(initial active inactive) ) {
-            push @{ $all{ $type } }, @{ $lifecycle->{ $type } || [] };
-            push @res,               @{ $lifecycle->{ $type } || [] };
+        foreach my $category ( qw(initial active inactive) ) {
+            my @vals = @{ $lifecycle->{ $category } ||= [] };
+            push @{ $LIFECYCLES_TYPES{$type}{$category} }, @vals;
+            push @res,                                     @vals;
         }
 
         my %seen;
@@ -623,12 +639,15 @@ sub FillCache {
             $lifecycle->{'transitions'}{''} = [ grep $_ ne 'deleted', @res ];
         }
     }
-    foreach my $type ( qw(initial active inactive), '' ) {
-        my %seen;
-        @{ $all{ $type } } = grep !$seen{ lc $_ }++, @{ $all{ $type } };
-        push @{ $all{''} }, @{ $all{ $type } } if $type;
+    for my $type (keys %LIFECYCLES_TYPES) {
+        for my $category ( qw(initial active inactive), '' ) {
+            my %seen;
+            @{ $LIFECYCLES_TYPES{$type}{$category} } =
+                grep !$seen{ lc $_ }++, @{ $LIFECYCLES_TYPES{$type}{$category} };
+            push @{ $LIFECYCLES_TYPES{$type}{''} },
+                @{ $LIFECYCLES_TYPES{$type}{$category} } if $category;
+        }
     }
-    $LIFECYCLES_CACHE{''} = \%all;
 
     foreach my $lifecycle ( values %LIFECYCLES_CACHE ) {
         my @res;
diff --git a/lib/RT/Queue.pm b/lib/RT/Queue.pm
index 2bc8f20..b161411 100644
--- a/lib/RT/Queue.pm
+++ b/lib/RT/Queue.pm
@@ -243,17 +243,17 @@ sub RightCategories {
 
 sub Lifecycle {
     my $self = shift;
-    unless (ref $self && $self->id) { 
-        return RT::Lifecycle->Load('')
+    unless (ref $self && $self->id) {
+        return RT::Lifecycle->Load( Type => 'ticket')
     }
 
     my $name = $self->_Value( Lifecycle => @_ );
     $name ||= 'default';
 
-    my $res = RT::Lifecycle->Load( $name );
+    my $res = RT::Lifecycle->Load( Name => $name );
     unless ( $res ) {
         $RT::Logger->error("Lifecycle '$name' for queue '".$self->Name."' doesn't exist");
-        return RT::Lifecycle->Load('default');
+        return RT::Lifecycle->Load( Name => 'default');
     }
     return $res;
 }
@@ -278,7 +278,7 @@ lifecycle is configured. Returns undef otherwise.
 sub ValidateLifecycle {
     my $self = shift;
     my $value = shift;
-    return undef unless RT::Lifecycle->Load( $value );
+    return undef unless RT::Lifecycle->Load( Name => $value );
     return 1;
 }
 
diff --git a/share/html/Elements/QueueSummaryByLifecycle b/share/html/Elements/QueueSummaryByLifecycle
index 1410e82..79e3a59 100644
--- a/share/html/Elements/QueueSummaryByLifecycle
+++ b/share/html/Elements/QueueSummaryByLifecycle
@@ -106,7 +106,7 @@ $m->callback( CallbackName => 'Filter', Queues => \@queues );
 my %lifecycle;
 
 for my $queue (@queues) {
-    my $cycle = RT::Lifecycle->Load( $queue->{'Lifecycle'} );
+    my $cycle = RT::Lifecycle->Load( Name => $queue->{'Lifecycle'} );
     $lifecycle{ lc $cycle->Name } = $cycle;
 }
 
diff --git a/share/html/Elements/QueueSummaryByStatus b/share/html/Elements/QueueSummaryByStatus
index f649d28..cd8b277 100644
--- a/share/html/Elements/QueueSummaryByStatus
+++ b/share/html/Elements/QueueSummaryByStatus
@@ -104,7 +104,7 @@ $m->callback( CallbackName => 'Filter', Queues => \@queues );
 my %lifecycle;
 
 for my $queue (@queues) {
-    my $cycle = RT::Lifecycle->Load( $queue->{'Lifecycle'} );
+    my $cycle = RT::Lifecycle->Load( Name => $queue->{'Lifecycle'} );
     $lifecycle{ lc $cycle->Name } = $cycle;
 }
 
diff --git a/t/lifecycles/types.t b/t/lifecycles/types.t
new file mode 100644
index 0000000..047340b
--- /dev/null
+++ b/t/lifecycles/types.t
@@ -0,0 +1,29 @@
+use strict;
+use warnings;
+
+BEGIN {require  't/lifecycles/utils.pl'};
+
+is_deeply( [ RT::Lifecycle->List], [qw/ approvals default delivery /],
+       "Get the list of lifecycles (implicitly for for tickets)");
+is_deeply( [ RT::Lifecycle->List('ticket') ],  [qw/ approvals default delivery /],
+       "Get the list of lifecycles for tickets");
+is_deeply( [ RT::Lifecycle->List('racecar') ], [qw/ racing /],
+       "Get the list of lifecycles for other types");
+
+my $tickets = RT::Lifecycle->Load( Name => '', Type => 'ticket' );
+ok($tickets, "Got a generalized lifecycle for tickets");
+isa_ok( $tickets, "RT::Lifecycle::Ticket", "Is the right subclass" );
+is_deeply( [ sort $tickets->Valid ],
+           [ sort qw(new open stalled resolved rejected deleted ordered),
+             'on way', 'delayed', 'delivered' ],
+           "Only gets ticket statuses" );
+
+
+my $racecars = RT::Lifecycle->Load( Name => '', Type => 'racecar' );
+ok($racecars, "Got a generalized lifecycle for racecars");
+isa_ok( $racecars, "RT::Lifecycle", "Is the generalized subclass" );
+is_deeply( [ sort $racecars->Valid ],
+           [ sort ('on-your-mark', 'get-set', 'go', 'first', 'second', 'third', 'no-place') ],
+           "Only gets racecar statuses" );
+
+done_testing;
diff --git a/t/lifecycles/utils.pl b/t/lifecycles/utils.pl
index ec3289f..4dbba23 100644
--- a/t/lifecycles/utils.pl
+++ b/t/lifecycles/utils.pl
@@ -63,6 +63,11 @@ Set(\%Lifecycles,
             'delayed -> on way'   => {label => 'Put On Way', update => 'Respond'},
         },
     },
+    racing => {
+        type => 'racecar',
+        active => ['on-your-mark', 'get-set', 'go'],
+        inactive => ['first', 'second', 'third', 'no-place'],
+    },
 );
 END
 }

commit 3db01a98612379861c5b7451d7cd4af5f4352708
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Wed Dec 12 15:38:27 2012 -0500

    Move Lifecycle to being a property of the ticket, not just the queue

diff --git a/lib/RT/Action/AutoOpen.pm b/lib/RT/Action/AutoOpen.pm
index 5f96e06..5112dba 100644
--- a/lib/RT/Action/AutoOpen.pm
+++ b/lib/RT/Action/AutoOpen.pm
@@ -87,7 +87,7 @@ sub Prepare {
 
     # no change if the ticket is in initial status and the message is a mail
     # from a requestor
-    return 1 if $ticket->QueueObj->Lifecycle->IsInitial($ticket->Status)
+    return 1 if $ticket->Lifecycle->IsInitial($ticket->Status)
         && $self->TransactionObj->IsInbound;
 
     if ( my $msg = $self->TransactionObj->Message->First ) {
diff --git a/lib/RT/Action/SetStatus.pm b/lib/RT/Action/SetStatus.pm
index f52d401..571ac05 100644
--- a/lib/RT/Action/SetStatus.pm
+++ b/lib/RT/Action/SetStatus.pm
@@ -101,7 +101,7 @@ sub Prepare {
     my $self = shift;
 
     my $ticket = $self->TicketObj;
-    my $lifecycle = $ticket->QueueObj->Lifecycle;
+    my $lifecycle = $ticket->Lifecycle;
     my $status = $ticket->Status;
 
     my $argument = $self->Argument;
diff --git a/lib/RT/Approval/Rule/Passed.pm b/lib/RT/Approval/Rule/Passed.pm
index 000a8dc..19f790e 100644
--- a/lib/RT/Approval/Rule/Passed.pm
+++ b/lib/RT/Approval/Rule/Passed.pm
@@ -96,7 +96,7 @@ sub Commit {
     $top->Correspond( MIMEObj => $template->MIMEObj );
 
     if ($passed) {
-        my $new_status = $top->QueueObj->Lifecycle->DefaultStatus('approved') || 'open';
+        my $new_status = $top->Lifecycle->DefaultStatus('approved') || 'open';
         if ( $new_status ne $top->Status ) {
             $top->SetStatus( $new_status );
         }
diff --git a/lib/RT/Approval/Rule/Rejected.pm b/lib/RT/Approval/Rule/Rejected.pm
index b22df5c..387b763 100644
--- a/lib/RT/Approval/Rule/Rejected.pm
+++ b/lib/RT/Approval/Rule/Rejected.pm
@@ -75,7 +75,7 @@ sub Commit {    # XXX: from custom prepare code
 
         $rejected->Correspond( MIMEObj => $template->MIMEObj );
         $rejected->SetStatus(
-            Status => $rejected->QueueObj->Lifecycle->DefaultStatus('denied') || 'rejected',
+            Status => $rejected->Lifecycle->DefaultStatus('denied') || 'rejected',
             Force  => 1,
         );
     }
diff --git a/lib/RT/Condition/StatusChange.pm b/lib/RT/Condition/StatusChange.pm
index 1de7a8c..f3a267b 100644
--- a/lib/RT/Condition/StatusChange.pm
+++ b/lib/RT/Condition/StatusChange.pm
@@ -118,7 +118,7 @@ sub IsApplicable {
         return 0;
     }
 
-    my $lifecycle = $self->TicketObj->QueueObj->Lifecycle;
+    my $lifecycle = $self->TicketObj->Lifecycle;
     if ( $new_must_be ) {
         return 0 unless grep lc($new) eq lc($_),
             map {m/^(initial|active|inactive)$/i? $lifecycle->Valid(lc $_): $_ }
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 184c1c9..84faed5 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -2551,7 +2551,7 @@ sub ProcessTicketReminders {
 
     if ( $args->{'update-reminders'} ) {
         while ( my $reminder = $reminder_collection->Next ) {
-            my $resolve_status = $reminder->QueueObj->Lifecycle->ReminderStatusOnResolve;
+            my $resolve_status = $reminder->Lifecycle->ReminderStatusOnResolve;
             my ( $status, $msg, $old_subject, @subresults );
             if (   $reminder->Status ne $resolve_status
                 && $args->{ 'Complete-Reminder-' . $reminder->id } )
diff --git a/lib/RT/Reminders.pm b/lib/RT/Reminders.pm
index 593d8df..a901384 100644
--- a/lib/RT/Reminders.pm
+++ b/lib/RT/Reminders.pm
@@ -149,7 +149,7 @@ sub Open {
     my $reminder = shift;
 
     my ( $status, $msg ) =
-      $reminder->SetStatus( $reminder->QueueObj->Lifecycle->ReminderStatusOnOpen );
+      $reminder->SetStatus( $reminder->Lifecycle->ReminderStatusOnOpen );
     $self->TicketObj->_NewTransaction(
         Type => 'OpenReminder',
         Field => 'RT::Ticket',
@@ -162,7 +162,7 @@ sub Resolve {
     my $self = shift;
     my $reminder = shift;
     my ( $status, $msg ) =
-      $reminder->SetStatus( $reminder->QueueObj->Lifecycle->ReminderStatusOnResolve );
+      $reminder->SetStatus( $reminder->Lifecycle->ReminderStatusOnResolve );
     $self->TicketObj->_NewTransaction(
         Type => 'ResolveReminder',
         Field => 'RT::Ticket',
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index c643a2d..4d20523 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -1371,7 +1371,7 @@ sub SetQueue {
     }
 
     my $new_status;
-    my $old_lifecycle = $self->QueueObj->Lifecycle;
+    my $old_lifecycle = $self->Lifecycle;
     my $new_lifecycle = $NewQueueObj->Lifecycle;
     if ( $old_lifecycle->Name ne $new_lifecycle->Name ) {
         unless ( $old_lifecycle->HasMoveMap( $new_lifecycle ) ) {
@@ -1524,6 +1524,17 @@ sub ResolvedObj {
     return $time;
 }
 
+=head2 Lifecycle
+
+Returns the L<RT::Lifecycle> for this ticket, which is picked up from
+the ticket's current queue.
+
+=cut
+
+sub Lifecycle {
+    my $self = shift;
+    return $self->QueueObj->Lifecycle;
+}
 
 =head2 FirstActiveStatus
 
@@ -1537,7 +1548,7 @@ This is used in L<RT::Action::AutoOpen>, for instance.
 sub FirstActiveStatus {
     my $self = shift;
 
-    my $lifecycle = $self->QueueObj->Lifecycle;
+    my $lifecycle = $self->Lifecycle;
     my $status = $self->Status;
     my @active = $lifecycle->Active;
     # no change if no active statuses in the lifecycle
@@ -1562,7 +1573,7 @@ This is used in resolve action in UnsafeEmailCommands, for instance.
 sub FirstInactiveStatus {
     my $self = shift;
 
-    my $lifecycle = $self->QueueObj->Lifecycle;
+    my $lifecycle = $self->Lifecycle;
     my $status = $self->Status;
     my @inactive = $lifecycle->Inactive;
     # no change if no inactive statuses in the lifecycle
@@ -2245,7 +2256,7 @@ sub _MergeInto {
     }
 
 
-    my $force_status = $self->QueueObj->Lifecycle->DefaultOnMerge;
+    my $force_status = $self->Lifecycle->DefaultOnMerge;
     if ( $force_status && $force_status ne $self->__Value('Status') ) {
         my ( $status_val, $status_msg )
             = $self->__Set( Field => 'Status', Value => $force_status );
@@ -2650,7 +2661,7 @@ sub SetStatus {
     $args{SetStarted} = 1 unless exists $args{SetStarted};
 
 
-    my $lifecycle = $self->QueueObj->Lifecycle;
+    my $lifecycle = $self->Lifecycle;
 
     my $new = $args{'Status'};
     unless ( $lifecycle->IsValid( $new ) ) {
@@ -2687,7 +2698,7 @@ sub _SetStatus {
         Status => undef,
         SetStarted => 1,
         RecordTransaction => 1,
-        Lifecycle => $self->QueueObj->Lifecycle,
+        Lifecycle => $self->Lifecycle,
         @_,
     );
     $args{NewLifecycle} ||= $args{Lifecycle};
@@ -2745,7 +2756,7 @@ Takes no arguments. Marks this ticket for garbage collection
 
 sub Delete {
     my $self = shift;
-    unless ( $self->QueueObj->Lifecycle->IsValid('deleted') ) {
+    unless ( $self->Lifecycle->IsValid('deleted') ) {
         return (0, $self->loc('Delete operation is disabled by lifecycle configuration') ); #loc
     }
     return ( $self->SetStatus('deleted') );
diff --git a/share/html/Approvals/Elements/Approve b/share/html/Approvals/Elements/Approve
index b33757a..d557e2b 100644
--- a/share/html/Approvals/Elements/Approve
+++ b/share/html/Approvals/Elements/Approve
@@ -106,5 +106,5 @@ $ticket => undef
 </%ARGS>
 <%INIT>
 my $status = $ticket->Status;
-my $inactive = $ticket->QueueObj->Lifecycle->IsInactive( $status );
+my $inactive = $ticket->Lifecycle->IsInactive( $status );
 </%INIT>
diff --git a/share/html/Elements/SelectStatus b/share/html/Elements/SelectStatus
index fa3f25a..8d37516 100644
--- a/share/html/Elements/SelectStatus
+++ b/share/html/Elements/SelectStatus
@@ -75,7 +75,7 @@ elsif ( $TicketObj ) {
     my $current = $TicketObj->Status;
     push @status, $current;
 
-    my $lifecycle = $TicketObj->QueueObj->Lifecycle;
+    my $lifecycle = $TicketObj->Lifecycle;
 
     my %has = ();
     foreach my $next ( $lifecycle->Transitions( $current ) ) {
diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 4624095..513932b 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -647,7 +647,7 @@ my $build_main_nav = sub {
                     && $obj->HasUnresolvedDependencies;
 
                 my $current   = $obj->Status;
-                my $lifecycle = $obj->QueueObj->Lifecycle;
+                my $lifecycle = $obj->Lifecycle;
                 my $i         = 1;
                 foreach my $info ( $lifecycle->Actions($current) ) {
                     my $next = $info->{'to'};
diff --git a/share/html/Ticket/Elements/Reminders b/share/html/Ticket/Elements/Reminders
index c12159e..cf36c66 100644
--- a/share/html/Ticket/Elements/Reminders
+++ b/share/html/Ticket/Elements/Reminders
@@ -55,7 +55,7 @@ $ShowSave => 1
 <%init>
 
 $Ticket = LoadTicket($id) if ($id);
-my $resolve_status = $Ticket->QueueObj->Lifecycle->ReminderStatusOnResolve;
+my $resolve_status = $Ticket->Lifecycle->ReminderStatusOnResolve;
 
 my $count_reminders = RT::Reminders->new($session{'CurrentUser'});
 $count_reminders->Ticket($Ticket->id);
@@ -154,10 +154,10 @@ $Index
 <td class="entry">
 % unless ( $Reminder->CurrentUserHasRight('ModifyTicket') ) {
 <input name="Complete-Reminder-<% $Reminder->id %>" type="hidden" 
-value=<% $Reminder->Status eq $Reminder->QueueObj->Lifecycle->ReminderStatusOnResolve ? 1 : 0 %> />
+value=<% $Reminder->Status eq $Reminder->Lifecycle->ReminderStatusOnResolve ? 1 : 0 %> />
 % }
 
-<input type="checkbox" value="1" name="Complete-Reminder-<% $Reminder->id %>" <% $Reminder->Status eq $Reminder->QueueObj->Lifecycle->ReminderStatusOnResolve ? 'checked="checked"' : '' |n %> 
+<input type="checkbox" value="1" name="Complete-Reminder-<% $Reminder->id %>" <% $Reminder->Status eq $Reminder->Lifecycle->ReminderStatusOnResolve ? 'checked="checked"' : '' |n %>
 % unless ( $Reminder->CurrentUserHasRight('ModifyTicket') ) {
 disabled="disabled" 
 % }
@@ -197,9 +197,9 @@ $Index
 <td class="collection-as-table">
 % unless ( $Reminder->CurrentUserHasRight('ModifyTicket') ) {
 <input name="Complete-Reminder-<% $Reminder->id %>" type="hidden" 
-value=<% $Reminder->Status eq $Reminder->QueueObj->Lifecycle->ReminderStatusOnResolve ? 1 : 0 %> />
+value=<% $Reminder->Status eq $Reminder->Lifecycle->ReminderStatusOnResolve ? 1 : 0 %> />
 % }
-<input type="checkbox" value="1" name="Complete-Reminder-<% $Reminder->id %>" <% $Reminder->Status eq $Reminder->QueueObj->Lifecycle->ReminderStatusOnResolve ? 'checked="checked"' : '' |n %> 
+<input type="checkbox" value="1" name="Complete-Reminder-<% $Reminder->id %>" <% $Reminder->Status eq $Reminder->Lifecycle->ReminderStatusOnResolve ? 'checked="checked"' : '' |n %>
 % unless ( $Reminder->CurrentUserHasRight('ModifyTicket') ) {
 disabled="disabled" 
 % }

commit 57592446c884e57e0e37a14f5ba430981740691f
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Wed Dec 12 15:39:55 2012 -0500

    Refactor status display into a more general component

diff --git a/share/html/Elements/SelectStatus b/share/html/Elements/SelectStatus
index 8d37516..fa0d8e5 100644
--- a/share/html/Elements/SelectStatus
+++ b/share/html/Elements/SelectStatus
@@ -69,39 +69,35 @@
 my %statuses_by_lifecycle;
 if ( @Statuses ) {
     $statuses_by_lifecycle{''} = \@Statuses;
-}
-elsif ( $TicketObj ) {
-    my @status;
-    my $current = $TicketObj->Status;
-    push @status, $current;
-
-    my $lifecycle = $TicketObj->Lifecycle;
+} else {
+    if ( $Object ) {
+        my $lifecycle = $Object->Lifecycle;
+        if ($Object->_Accessible("Status", "read")) {
+            my $current = $Object->Status;
+            my @status;
+            push @status, $current;
 
-    my %has = ();
-    foreach my $next ( $lifecycle->Transitions( $current ) ) {
-        my $check = $lifecycle->CheckRight( $current => $next );
-        $has{ $check } = $TicketObj->CurrentUserHasRight( $check )
-            unless exists $has{ $check };
-        push @status, $next if $has{ $check };
+            my %has = ();
+            foreach my $next ( $lifecycle->Transitions( $current ) ) {
+                my $check = $lifecycle->CheckRight( $current => $next );
+                $has{ $check } = $Object->CurrentUserHasRight( $check )
+                    unless exists $has{ $check };
+                push @status, $next if $has{ $check };
+            }
+            $statuses_by_lifecycle{$lifecycle->Name} = \@status;
+        } else {
+            $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Transitions('') ];
+        }
     }
-    $statuses_by_lifecycle{$lifecycle->Name} = \@status;
-}
-elsif ( $QueueObj ) {
-    my $lifecycle = $QueueObj->Lifecycle;
-    $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Transitions('') ];
-} elsif ( %Queues ) {
-    for my $id (keys %Queues) {
-        my $queue = RT::Queue->new($session{'CurrentUser'});
-        $queue->Load($id);
-        if ($queue->id) {
-            my $lifecycle = $queue->Lifecycle;
+    for my $lifecycle ( @Lifecycles ) {
+        $statuses_by_lifecycle{$lifecycle->Name} ||= [ $lifecycle->Valid ];
+    }
+
+    if (not keys %statuses_by_lifecycle) {
+        for my $lifecycle (map { RT::Lifecycle->Load($_) } RT::Lifecycle->List($Type)) {
             $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Valid ];
         }
     }
-} else {
-    for my $lifecycle (map { RT::Lifecycle->Load($_) } RT::Lifecycle->List) {
-        $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Valid ];
-    }
 }
 
 if (keys %statuses_by_lifecycle) {
@@ -131,11 +127,11 @@ my $group_by_lifecycle = keys %statuses_by_lifecycle > 1;
 </%INIT>
 <%ARGS>
 $Name => undef
+$Type => undef,
 
 @Statuses => ()
-$TicketObj => undef
-$QueueObj => undef
-%Queues => ()
+$Object => undef,
+ at Lifecycles => (),
 
 $Default => ''
 $SkipDeleted => 0
diff --git a/share/html/Search/Bulk.html b/share/html/Search/Bulk.html
index 7eec717..66a168d 100644
--- a/share/html/Search/Bulk.html
+++ b/share/html/Search/Bulk.html
@@ -105,7 +105,7 @@
 <tr><td class="label"> <&|/l&>Make queue</&>: </td>
 <td class="value"> <& /Elements/SelectQueue, Name => "Queue", Default => $ARGS{Queue} &> </td></tr>
 <tr><td class="label"> <&|/l&>Make Status</&>: </td>
-<td class="value"> <& /Elements/SelectStatus, Name => "Status", Default => $ARGS{Status}, Queues => $seen_queues &> </td></tr>
+<td class="value"> <& /Ticket/Elements/SelectStatus, Name => "Status", Default => $ARGS{Status}, Queues => $seen_queues &> </td></tr>
 <tr><td class="label"> <&|/l&>Make date Starts</&>: </td>
 <td class="value"> <& /Elements/SelectDate, Name => "Starts_Date", Default => $ARGS{Starts_Date} || '' &> </td></tr>
 <tr><td class="label"> <&|/l&>Make date Started</&>: </td>
diff --git a/share/html/Search/Elements/PickBasics b/share/html/Search/Elements/PickBasics
index ce31b96..80b5349 100644
--- a/share/html/Search/Elements/PickBasics
+++ b/share/html/Search/Elements/PickBasics
@@ -102,7 +102,7 @@ my @lines = (
         },
         Value => {
             Type => 'component',
-            Path => '/Elements/SelectStatus',
+            Path => '/Ticket/Elements/SelectStatus',
             Arguments => { SkipDeleted => 1, Queues => \%queues },
         },
     },
diff --git a/share/html/SelfService/Update.html b/share/html/SelfService/Update.html
index b2c4fe4..3ff91ae 100644
--- a/share/html/SelfService/Update.html
+++ b/share/html/SelfService/Update.html
@@ -60,7 +60,7 @@
             <&|/l&>Status</&>
         </td>
         <td class="value">
-            <& /Elements/SelectStatus, Name => "Status", TicketObj => $Ticket, DefaultLabel => loc("[_1] (Unchanged)", loc($Ticket->Status)), Default => $ARGS{'Status'} || ($Ticket->Status eq $DefaultStatus ? undef : $DefaultStatus)&>
+            <& /Ticket/Elements/SelectStatus, Name => "Status", TicketObj => $Ticket, DefaultLabel => loc("[_1] (Unchanged)", loc($Ticket->Status)), Default => $ARGS{'Status'} || ($Ticket->Status eq $DefaultStatus ? undef : $DefaultStatus)&>
         </td>
     </tr>
     <tr>
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index 302d915..884a5f0 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -78,7 +78,7 @@
                 },
             },
             {   name => 'Status',
-                comp => '/Elements/SelectStatus',
+                comp => '/Ticket/Elements/SelectStatus',
                 args => {
                     Name            => "Status",
                     Default         => $ARGS{Status} || $QueueObj->Lifecycle->DefaultOnCreate,
diff --git a/share/html/Ticket/Elements/EditBasics b/share/html/Ticket/Elements/EditBasics
index b428aab..47aa494 100644
--- a/share/html/Ticket/Elements/EditBasics
+++ b/share/html/Ticket/Elements/EditBasics
@@ -58,7 +58,7 @@ unless ( @fields ) {
             html => '<input name="Subject" value="'.$m->interp->apply_escapes( $defaults{'Subject'} || $TicketObj->Subject, 'h' ).'" />',
         },
         {   name => 'Status',
-            comp => '/Elements/SelectStatus',
+            comp => '/Ticket/Elements/SelectStatus',
             args => {
                 Name => 'Status',
                 DefaultLabel => loc("[_1] (Unchanged)",loc($TicketObj->Status)),
diff --git a/share/html/SelfService/Update.html b/share/html/Ticket/Elements/SelectStatus
similarity index 52%
copy from share/html/SelfService/Update.html
copy to share/html/Ticket/Elements/SelectStatus
index b2c4fe4..3334d11 100644
--- a/share/html/SelfService/Update.html
+++ b/share/html/Ticket/Elements/SelectStatus
@@ -45,68 +45,24 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<& /SelfService/Elements/Header, 
-    Title =>loc('Update ticket #[_1]', $Ticket->id) 
+<& /Elements/SelectStatus,
+   %ARGS,
+   Statuses => \@Statuses,
+   Object => $TicketObj || $QueueObj,
+   Lifecycles => \@Lifecycles,
+   Type => 'ticket',
 &>
-
-% $m->callback(CallbackName => 'BeforeForm', %ARGS, ARGSRef => \%ARGS, Ticket => $Ticket );
-
-<form action="Display.html" method="post" enctype="multipart/form-data">
-<input type="hidden" class="hidden" name="UpdateType" value="response" />
-<input type="hidden" class="hidden" name="id" value="<%$Ticket->Id%>" />
-<table width="100%">
-    <tr>
-        <td class="label">
-            <&|/l&>Status</&>
-        </td>
-        <td class="value">
-            <& /Elements/SelectStatus, Name => "Status", TicketObj => $Ticket, DefaultLabel => loc("[_1] (Unchanged)", loc($Ticket->Status)), Default => $ARGS{'Status'} || ($Ticket->Status eq $DefaultStatus ? undef : $DefaultStatus)&>
-        </td>
-    </tr>
-    <tr>
-        <td class="label">
-            <&|/l&>Subject</&>
-        </td>
-        <td class="value">
-            <input name="UpdateSubject" size="60" value="<% $Ticket->Subject %>" />
-        </td>
-
-    </tr>
-    <& /Ticket/Elements/AddAttachments, %ARGS, TicketObj => $Ticket &>
-    <tr><td colspan="2"><& /Elements/EditCustomFields, Object => $Ticket, AsTable => 0 &></td></tr>
-</table>
-<& /Elements/MessageBox, 
-    Name => "UpdateContent", 
-    QuoteTransaction => $ARGS{QuoteTransaction} 
-    &>
-    <br />
-
-
-<& /Elements/Submit &>
-  </form>
-
-
-
 <%INIT>
-
-my $Ticket = LoadTicket($id);
-
-$m->callback( Ticket => $Ticket, ARGSRef => \%ARGS, CallbackName => 'Initial' ); 
-
-my $title = loc( "Update ticket #[_1]", $Ticket->id );
-
-$DefaultStatus = $ARGS{Status} || $Ticket->Status() unless ($DefaultStatus);
-
-
-Abort( loc("No permission to view update ticket") )
-  unless ( $Ticket->CurrentUserHasRight('ReplyToTicket')
-    or $Ticket->CurrentUserHasRight('ModifyTicket') );
-
-$m->callback(CallbackName => 'BeforeDisplay', Ticket => \$Ticket, ARGSRef => \%ARGS);
+my @Lifecycles;
+for my $id (keys %Queues) {
+    my $queue = RT::Queue->new($session{'CurrentUser'});
+    $queue->Load($id);
+    push @Lifecycles, $queue->Lifecycle if $queue->id;
+}
 </%INIT>
-
 <%ARGS>
-$id => undef
-$Action => undef
-$DefaultStatus => undef
+ at Statuses => ()
+$TicketObj => undef
+$QueueObj => undef
+%Queues => ()
 </%ARGS>
diff --git a/share/html/Ticket/Update.html b/share/html/Ticket/Update.html
index 5734f95..1ab66c6 100644
--- a/share/html/Ticket/Update.html
+++ b/share/html/Ticket/Update.html
@@ -104,7 +104,7 @@
     InTable   => 1,
     fields    => [
         {   name => 'Status',
-            comp => '/Elements/SelectStatus',
+            comp => '/Ticket/Elements/SelectStatus',
             args => {
                 Name => 'Status',
                 DefaultLabel => loc("[_1] (Unchanged)", loc($TicketObj->Status)),
diff --git a/share/html/Tools/MyDay.html b/share/html/Tools/MyDay.html
index dee4bab..e15e586 100644
--- a/share/html/Tools/MyDay.html
+++ b/share/html/Tools/MyDay.html
@@ -64,7 +64,7 @@ href="<%RT->Config->Get('WebPath')%>/Ticket/Display.html?id=<%$Ticket->Id%>"><%$
 <td rowspan="2"><span class="label"><&|/l&>Comments</&>:<br /></span><textarea name="UpdateContent-<%$Ticket->Id%>" rows="5"
 cols="60"></textarea></td></tr>
 <tr <%$class|n%>>
-<td><span class="label"><&|/l&>Status</&>:</span> <& /Elements/SelectStatus, Name=> 'UpdateStatus-'.$Ticket->Id, TicketObj => $Ticket, 
+<td><span class="label"><&|/l&>Status</&>:</span> <& /Ticket/Elements/SelectStatus, Name=> 'UpdateStatus-'.$Ticket->Id, TicketObj => $Ticket, 
 				  DefaultLabel => loc("[_1] (Unchanged)",loc($Ticket->Status())) &></td>
                   </tr>
 
diff --git a/share/html/m/ticket/create b/share/html/m/ticket/create
index e3885ff..2e662c0 100644
--- a/share/html/m/ticket/create
+++ b/share/html/m/ticket/create
@@ -274,7 +274,7 @@ $showrows->(
     loc('Status') =>
 
         $m->scomp(
-        "/Elements/SelectStatus",
+        "/Ticket/Elements/SelectStatus",
         Name         => "Status",
         QueueObj     => $QueueObj,
         Default      => $ARGS{Status} || $QueueObj->Lifecycle->DefaultOnCreate,
diff --git a/share/html/m/ticket/reply b/share/html/m/ticket/reply
index 18587bc..159c719 100644
--- a/share/html/m/ticket/reply
+++ b/share/html/m/ticket/reply
@@ -61,7 +61,7 @@
 
 <div class="entry"><span class="label"><&|/l&>Status</&>:</span>
 <div class="value">
-<& /Elements/SelectStatus, Name=>"Status", TicketObj => $t, DefaultLabel => loc("[_1] (Unchanged)", loc($t->Status)), Default => $ARGS{'Status'} || ($t->Status eq $DefaultStatus ? undef : $DefaultStatus)&>
+<& /Ticket/Elements/SelectStatus, Name=>"Status", TicketObj => $t, DefaultLabel => loc("[_1] (Unchanged)", loc($t->Status)), Default => $ARGS{'Status'} || ($t->Status eq $DefaultStatus ? undef : $DefaultStatus)&>
 </div></div>
 
 <div class="entry"><span class="label"><&|/l&>Owner</&>:</span>

commit cb4997a4bd79677afe21ccdc99e9e0c8e544e83d
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Thu Dec 13 20:19:15 2012 -0500

    Refactor repeated logic into /Ticket/Elements/SelectStatus

diff --git a/share/html/SelfService/Update.html b/share/html/SelfService/Update.html
index 3ff91ae..002acc3 100644
--- a/share/html/SelfService/Update.html
+++ b/share/html/SelfService/Update.html
@@ -60,7 +60,10 @@
             <&|/l&>Status</&>
         </td>
         <td class="value">
-            <& /Ticket/Elements/SelectStatus, Name => "Status", TicketObj => $Ticket, DefaultLabel => loc("[_1] (Unchanged)", loc($Ticket->Status)), Default => $ARGS{'Status'} || ($Ticket->Status eq $DefaultStatus ? undef : $DefaultStatus)&>
+            <& /Ticket/Elements/SelectStatus,
+               Name => "Status",
+               TicketObj => $Ticket,
+               Default => $DefaultStatus &>
         </td>
     </tr>
     <tr>
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index 884a5f0..4f9ebc4 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -81,9 +81,6 @@
                 comp => '/Ticket/Elements/SelectStatus',
                 args => {
                     Name            => "Status",
-                    Default         => $ARGS{Status} || $QueueObj->Lifecycle->DefaultOnCreate,
-                    DefaultValue    => 0,
-                    SkipDeleted     => 1,
                     QueueObj        => $QueueObj,
                 },
             },
diff --git a/share/html/Ticket/Elements/EditBasics b/share/html/Ticket/Elements/EditBasics
index 47aa494..1eea2fc 100644
--- a/share/html/Ticket/Elements/EditBasics
+++ b/share/html/Ticket/Elements/EditBasics
@@ -61,10 +61,9 @@ unless ( @fields ) {
             comp => '/Ticket/Elements/SelectStatus',
             args => {
                 Name => 'Status',
-                DefaultLabel => loc("[_1] (Unchanged)",loc($TicketObj->Status)),
-                Default => $defaults{'Status'} || undef,
+                Default => $defaults{Status},
+                DefaultFromArgs => 0,
                 TicketObj => $TicketObj,
-                QueueObj => $TicketObj->QueueObj,
             },
         },
         {   name => 'Queue',
diff --git a/share/html/Ticket/Elements/SelectStatus b/share/html/Ticket/Elements/SelectStatus
index 3334d11..f1e3cad 100644
--- a/share/html/Ticket/Elements/SelectStatus
+++ b/share/html/Ticket/Elements/SelectStatus
@@ -59,8 +59,21 @@ for my $id (keys %Queues) {
     $queue->Load($id);
     push @Lifecycles, $queue->Lifecycle if $queue->id;
 }
+
+if ($TicketObj) {
+    $ARGS{DefaultLabel} = loc("[_1] (Unchanged)", loc($TicketObj->Status));
+    if ($DefaultFromArgs and $DECODED_ARGS->{Status}) {
+        $ARGS{Default} = $DECODED_ARGS->{Status};
+    } elsif (defined $ARGS{Default}) {
+        $ARGS{Default} = undef if $TicketObj->Status eq $ARGS{Default};
+    }
+} elsif ($QueueObj) {
+    $ARGS{DefaultValue} = 0;
+    $ARGS{Default} ||= $DECODED_ARGS->{Status} || $QueueObj->Lifecycle->DefaultOnCreate;
+}
 </%INIT>
 <%ARGS>
+$DefaultFromArgs => 1,
 @Statuses => ()
 $TicketObj => undef
 $QueueObj => undef
diff --git a/share/html/Ticket/Update.html b/share/html/Ticket/Update.html
index 1ab66c6..7a821d4 100644
--- a/share/html/Ticket/Update.html
+++ b/share/html/Ticket/Update.html
@@ -107,10 +107,8 @@
             comp => '/Ticket/Elements/SelectStatus',
             args => {
                 Name => 'Status',
-                DefaultLabel => loc("[_1] (Unchanged)", loc($TicketObj->Status)),
-                Default => $ARGS{'Status'} || ($TicketObj->Status eq $DefaultStatus ? undef : $DefaultStatus),
+                Default => $DefaultStatus,
                 TicketObj => $TicketObj,
-                QueueObj => $TicketObj->QueueObj
             },
         },
         {   name => 'Owner',
diff --git a/share/html/Tools/MyDay.html b/share/html/Tools/MyDay.html
index e15e586..121fafd 100644
--- a/share/html/Tools/MyDay.html
+++ b/share/html/Tools/MyDay.html
@@ -64,8 +64,7 @@ href="<%RT->Config->Get('WebPath')%>/Ticket/Display.html?id=<%$Ticket->Id%>"><%$
 <td rowspan="2"><span class="label"><&|/l&>Comments</&>:<br /></span><textarea name="UpdateContent-<%$Ticket->Id%>" rows="5"
 cols="60"></textarea></td></tr>
 <tr <%$class|n%>>
-<td><span class="label"><&|/l&>Status</&>:</span> <& /Ticket/Elements/SelectStatus, Name=> 'UpdateStatus-'.$Ticket->Id, TicketObj => $Ticket, 
-				  DefaultLabel => loc("[_1] (Unchanged)",loc($Ticket->Status())) &></td>
+<td><span class="label"><&|/l&>Status</&>:</span> <& /Ticket/Elements/SelectStatus, Name=> 'UpdateStatus-'.$Ticket->Id, TicketObj => $Ticket &></td>
                   </tr>
 
 % }
diff --git a/share/html/m/ticket/create b/share/html/m/ticket/create
index 2e662c0..43a2bf2 100644
--- a/share/html/m/ticket/create
+++ b/share/html/m/ticket/create
@@ -277,8 +277,6 @@ $showrows->(
         "/Ticket/Elements/SelectStatus",
         Name         => "Status",
         QueueObj     => $QueueObj,
-        Default      => $ARGS{Status} || $QueueObj->Lifecycle->DefaultOnCreate,
-        DefaultValue => 0,
         ),
 
     loc("Owner") =>
diff --git a/share/html/m/ticket/reply b/share/html/m/ticket/reply
index 159c719..afeb870 100644
--- a/share/html/m/ticket/reply
+++ b/share/html/m/ticket/reply
@@ -61,7 +61,10 @@
 
 <div class="entry"><span class="label"><&|/l&>Status</&>:</span>
 <div class="value">
-<& /Ticket/Elements/SelectStatus, Name=>"Status", TicketObj => $t, DefaultLabel => loc("[_1] (Unchanged)", loc($t->Status)), Default => $ARGS{'Status'} || ($t->Status eq $DefaultStatus ? undef : $DefaultStatus)&>
+<& /Ticket/Elements/SelectStatus,
+   Name=>"Status",
+   TicketObj => $t,
+   Default => $DefaultStatus &>
 </div></div>
 
 <div class="entry"><span class="label"><&|/l&>Owner</&>:</span>

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


More information about the Rt-commit mailing list